From 10b5b80675f41302f9a25eb59cfe4b97745dccc3 Mon Sep 17 00:00:00 2001 From: "T. van der Zwan" Date: Fri, 26 Jul 2013 20:38:34 +0000 Subject: [PATCH] First working version with some test executables --- CMakeLists.txt | 38 + bin/install_hyperion.sh | 24 + cmake/FindPNG.cmake | 63 + config/Hyperion.config.json | 291 + config/Hyperion.schema.json | 134 + dependencies/CMakeLists.txt | 9 + dependencies/build/CMakeLists.txt | 9 + dependencies/build/jsoncpp/AUTHORS | 1 + dependencies/build/jsoncpp/CMakeLists.txt | 32 + dependencies/build/jsoncpp/LICENSE | 55 + .../build/jsoncpp/json_batchallocator.h | 130 + .../build/jsoncpp/json_internalarray.inl | 456 ++ .../build/jsoncpp/json_internalmap.inl | 615 +++ dependencies/build/jsoncpp/json_reader.cpp | 880 +++ dependencies/build/jsoncpp/json_tool.h | 93 + dependencies/build/jsoncpp/json_value.cpp | 1829 +++++++ .../build/jsoncpp/json_valueiterator.inl | 299 ++ dependencies/build/jsoncpp/json_writer.cpp | 838 +++ dependencies/build/jsoncpp/sconscript | 8 + dependencies/include/json/autolink.h | 24 + dependencies/include/json/config.h | 96 + dependencies/include/json/features.h | 49 + dependencies/include/json/forwards.h | 44 + dependencies/include/json/json.h | 15 + dependencies/include/json/reader.h | 214 + dependencies/include/json/value.h | 1103 ++++ dependencies/include/json/writer.h | 185 + include/boblight-functions.h | 41 + include/boblight.h | 103 + include/hyperion/Hyperion.h | 41 + include/hyperion/ImageToLedsMap.h | 27 + include/hyperion/LedDevice.h | 27 + include/hyperion/LedString.h | 72 + include/utils/RgbColor.h | 31 + include/utils/RgbImage.h | 56 + include/utils/jsonschema/JsonFactory.h | 66 + include/utils/jsonschema/JsonSchemaChecker.h | 75 + libsrc/CMakeLists.txt | 15 + libsrc/bob2hyperion.cpp | 138 + libsrc/hyperion/CMakeLists.txt | 17 + libsrc/hyperion/Hyperion.cpp | 101 + libsrc/hyperion/ImageToLedsMap.cpp | 90 + libsrc/hyperion/LedDeviceWs2801.cpp | 85 + libsrc/hyperion/LedDeviceWs2801.h | 33 + libsrc/hyperion/LedString.cpp | 60 + libsrc/hyperionpng/CMakeLists.txt | 19 + libsrc/hyperionpng/hyperion-png.cpp | 174 + libsrc/hyperionpng/pngwriter.cc | 4722 +++++++++++++++++ libsrc/hyperionpng/pngwriter.h | 745 +++ libsrc/utils/CMakeLists.txt | 19 + libsrc/utils/RgbColor.cpp | 10 + libsrc/utils/RgbImage.cpp | 51 + libsrc/utils/jsonschema/JsonSchemaChecker.cpp | 460 ++ src/CMakeLists.txt | 44 + src/FbWriter.h | 115 + src/Test2BobLight.cpp | 39 + src/TestBoblightOrig.cpp | 16 + src/TestConfigFile.cpp | 38 + src/TestHyperionPng.cpp | 43 + src/TestSpi.cpp | 161 + src/ViewPng.cpp | 155 + src/WriteConfig.cpp | 198 + 62 files changed, 15621 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 bin/install_hyperion.sh create mode 100644 cmake/FindPNG.cmake create mode 100644 config/Hyperion.config.json create mode 100644 config/Hyperion.schema.json create mode 100644 dependencies/CMakeLists.txt create mode 100644 dependencies/build/CMakeLists.txt create mode 100644 dependencies/build/jsoncpp/AUTHORS create mode 100644 dependencies/build/jsoncpp/CMakeLists.txt create mode 100644 dependencies/build/jsoncpp/LICENSE create mode 100644 dependencies/build/jsoncpp/json_batchallocator.h create mode 100644 dependencies/build/jsoncpp/json_internalarray.inl create mode 100644 dependencies/build/jsoncpp/json_internalmap.inl create mode 100644 dependencies/build/jsoncpp/json_reader.cpp create mode 100644 dependencies/build/jsoncpp/json_tool.h create mode 100644 dependencies/build/jsoncpp/json_value.cpp create mode 100644 dependencies/build/jsoncpp/json_valueiterator.inl create mode 100644 dependencies/build/jsoncpp/json_writer.cpp create mode 100644 dependencies/build/jsoncpp/sconscript create mode 100644 dependencies/include/json/autolink.h create mode 100644 dependencies/include/json/config.h create mode 100644 dependencies/include/json/features.h create mode 100644 dependencies/include/json/forwards.h create mode 100644 dependencies/include/json/json.h create mode 100644 dependencies/include/json/reader.h create mode 100644 dependencies/include/json/value.h create mode 100644 dependencies/include/json/writer.h create mode 100644 include/boblight-functions.h create mode 100644 include/boblight.h create mode 100644 include/hyperion/Hyperion.h create mode 100644 include/hyperion/ImageToLedsMap.h create mode 100644 include/hyperion/LedDevice.h create mode 100644 include/hyperion/LedString.h create mode 100644 include/utils/RgbColor.h create mode 100644 include/utils/RgbImage.h create mode 100644 include/utils/jsonschema/JsonFactory.h create mode 100644 include/utils/jsonschema/JsonSchemaChecker.h create mode 100644 libsrc/CMakeLists.txt create mode 100644 libsrc/bob2hyperion.cpp create mode 100644 libsrc/hyperion/CMakeLists.txt create mode 100644 libsrc/hyperion/Hyperion.cpp create mode 100644 libsrc/hyperion/ImageToLedsMap.cpp create mode 100644 libsrc/hyperion/LedDeviceWs2801.cpp create mode 100644 libsrc/hyperion/LedDeviceWs2801.h create mode 100644 libsrc/hyperion/LedString.cpp create mode 100644 libsrc/hyperionpng/CMakeLists.txt create mode 100644 libsrc/hyperionpng/hyperion-png.cpp create mode 100644 libsrc/hyperionpng/pngwriter.cc create mode 100644 libsrc/hyperionpng/pngwriter.h create mode 100644 libsrc/utils/CMakeLists.txt create mode 100644 libsrc/utils/RgbColor.cpp create mode 100644 libsrc/utils/RgbImage.cpp create mode 100644 libsrc/utils/jsonschema/JsonSchemaChecker.cpp create mode 100644 src/CMakeLists.txt create mode 100644 src/FbWriter.h create mode 100644 src/Test2BobLight.cpp create mode 100644 src/TestBoblightOrig.cpp create mode 100644 src/TestConfigFile.cpp create mode 100644 src/TestHyperionPng.cpp create mode 100644 src/TestSpi.cpp create mode 100644 src/ViewPng.cpp create mode 100644 src/WriteConfig.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..d8f15681 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,38 @@ + +# define the minimum cmake version (as required by cmake) +cmake_minimum_required(VERSION 2.8) +#set(CMAKE_TOOLCHAIN_FILE /opt/raspberrypi/Toolchain-RaspberryPi.cmake) + +# Define the main-project name +project(Hyperion) + +# Add project specific cmake modules (find, etc) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +# Define the global output path of binaries +SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) +SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +file(MAKE_DIRECTORY ${LIBRARY_OUTPUT_PATH}) +file(MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + +# Add the project include directory as additional include path +include_directories(${CMAKE_SOURCE_DIR}/dependencies/include) +include_directories(${CMAKE_SOURCE_DIR}/include) + +# Prefer static linking over dynamic +set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so") + +#set(CMAKE_BUILD_TYPE "Release") + +# enable C++11 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall") + +configure_file(bin/install_hyperion.sh ${LIBRARY_OUTPUT_PATH} @ONLY) +configure_file(config/Hyperion.config.json ${LIBRARY_OUTPUT_PATH} @ONLY) +configure_file(config/Hyperion.schema.json ${LIBRARY_OUTPUT_PATH} @ONLY) + +# Add the source/lib directories +add_subdirectory(dependencies) +add_subdirectory(libsrc) +add_subdirectory(src) diff --git a/bin/install_hyperion.sh b/bin/install_hyperion.sh new file mode 100644 index 00000000..0448b7a1 --- /dev/null +++ b/bin/install_hyperion.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# Script for removing the existing boblight library and replacing it with Hyperion + +# First stop the current BobLight demon and XBMC +initctl stop xbmc +initctl stop boblight + +# Install the RapsiLight library +cp libbob2hyperion.so /usr/lib/libbob2hyperion.so +chmod 755 /usr/lib/libbob2hyperion.so +cp hyperion.config.json /etc/ +cp hyperion.schema.json /etc/ + +# Remove the existing boblight client library (make backup) +cp /usr/lib/libboblight.so.0.0.0 /usr/lib/libboblight.old +# Rename the settings file to ensure that the boblight-deamon does not start +mv /etc/bobconfig.txt /etc/bobconfig.txt.old + +# Link libboblight to the new installed library +ln -s /usr/lib/libbob2hyperion.so /usr/lib/libboblight.so.0.0.0 + +# Restart only XBMC +initctl start xbmc diff --git a/cmake/FindPNG.cmake b/cmake/FindPNG.cmake new file mode 100644 index 00000000..614b4c04 --- /dev/null +++ b/cmake/FindPNG.cmake @@ -0,0 +1,63 @@ +# - Find the native PNG includes and library +# + +# This module defines +# PNG_INCLUDE_DIR, where to find png.h, etc. +# PNG_LIBRARIES, the libraries to link against to use PNG. +# PNG_DEFINITIONS - You should ADD_DEFINITONS(${PNG_DEFINITIONS}) before compiling code that includes png library files. +# PNG_FOUND, If false, do not try to use PNG. +# also defined, but not for general use are +# PNG_LIBRARY, where to find the PNG library. +# None of the above will be defined unles zlib can be found. +# PNG depends on Zlib +# +# Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. +# See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + +INCLUDE(FindZLIB) + +SET(PNG_FOUND "NO") + +IF(ZLIB_FOUND) + FIND_PATH(PNG_PNG_INCLUDE_DIR png.h + /usr/local/include + /usr/include + /usr/local/include/libpng # OpenBSD + ) + + SET(PNG_NAMES ${PNG_NAMES} png libpng) + FIND_LIBRARY(PNG_LIBRARY + NAMES ${PNG_NAMES} + PATHS /usr/lib64 /usr/lib /usr/local/lib + ) + + IF (PNG_LIBRARY AND PNG_PNG_INCLUDE_DIR) + # png.h includes zlib.h. Sigh. + SET(PNG_INCLUDE_DIR ${PNG_PNG_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ) + SET(PNG_LIBRARIES ${PNG_LIBRARY} ${ZLIB_LIBRARY}) + SET(PNG_FOUND "YES") + SET(HAVE_PNG_H) + IF (CYGWIN) + IF(BUILD_SHARED_LIBS) + # No need to define PNG_USE_DLL here, because it's default for Cygwin. + ELSE(BUILD_SHARED_LIBS) + SET (PNG_DEFINITIONS -DPNG_STATIC) + ENDIF(BUILD_SHARED_LIBS) + ENDIF (CYGWIN) + + ENDIF (PNG_LIBRARY AND PNG_PNG_INCLUDE_DIR) + +ENDIF(ZLIB_FOUND) + +IF (PNG_FOUND) + IF (NOT PNG_FIND_QUIETLY) + MESSAGE(STATUS "Found PNG: ${PNG_LIBRARY}") + ENDIF (NOT PNG_FIND_QUIETLY) +ELSE (PNG_FOUND) + IF (PNG_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find PNG library") + ENDIF (PNG_FIND_REQUIRED) +ENDIF (PNG_FOUND) + +MARK_AS_ADVANCED(PNG_PNG_INCLUDE_DIR PNG_LIBRARY ) diff --git a/config/Hyperion.config.json b/config/Hyperion.config.json new file mode 100644 index 00000000..8757064d --- /dev/null +++ b/config/Hyperion.config.json @@ -0,0 +1,291 @@ +// Automatically generated configuration file for 'Hyperion' +// Generation script: ./WriteConfig + +{ + "device" : + { + "name" : "MyPi", + "type" : "ws2801", + "output" : "/dev/spidev0.0", + "interval" : 20000, + "rate" : 48000 + }, + "color" : + { + "red" : + { + "gamma" : 1.0, + "adjust" : 1.0, + "blacklevel" : 0.0 + }, + "green" : + { + "gamma" : 1.0, + "adjust" : 1.0, + "blacklevel" : 0.0 + }, + "blue" : + { + "gamma" : 1.0, + "adjust" : 1.0, + "blacklevel" : 0.0 + } + }, + "leds" : + [ + { + "index" : 0, + "hscan" : { "minimum" : 47.0588, "maximum" : 52.9412 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 1, + "hscan" : { "minimum" : 41.1765, "maximum" : 47.0588 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 2, + "hscan" : { "minimum" : 35.2941, "maximum" : 41.1765 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 3, + "hscan" : { "minimum" : 29.4118, "maximum" : 35.2941 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 4, + "hscan" : { "minimum" : 23.5294, "maximum" : 29.4118 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 5, + "hscan" : { "minimum" : 17.6471, "maximum" : 23.5294 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 6, + "hscan" : { "minimum" : 11.7647, "maximum" : 17.6471 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 7, + "hscan" : { "minimum" : 5.88235, "maximum" : 11.7647 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + // TOP-LEFT Corner + { + "index" : 8, + "hscan" : { "minimum" : 0, "maximum" : 5.88235 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 9, + "hscan" : { "minimum" : 0, "maximum" : 10 }, + "vscan" : { "minimum" : 10, "maximum" : 20 } + }, + { + "index" : 10, + "hscan" : { "minimum" : 0, "maximum" : 10 }, + "vscan" : { "minimum" : 20, "maximum" : 30 } + }, + { + "index" : 11, + "hscan" : { "minimum" : 0, "maximum" : 10 }, + "vscan" : { "minimum" : 30, "maximum" : 40 } + }, + { + "index" : 12, + "hscan" : { "minimum" : 0, "maximum" : 10 }, + "vscan" : { "minimum" : 40, "maximum" : 50 } + }, + { + "index" : 13, + "hscan" : { "minimum" : 0, "maximum" : 10 }, + "vscan" : { "minimum" : 50, "maximum" : 60 } + }, + { + "index" : 14, + "hscan" : { "minimum" : 0, "maximum" : 10 }, + "vscan" : { "minimum" : 60, "maximum" : 70 } + }, + { + "index" : 15, + "hscan" : { "minimum" : 0, "maximum" : 10 }, + "vscan" : { "minimum" : 70, "maximum" : 80 } + }, + { + "index" : 16, + "hscan" : { "minimum" : 0, "maximum" : 10 }, + "vscan" : { "minimum" : 80, "maximum" : 90 } + }, + // BOTTOM-LEFT Corner + { + "index" : 17, + "hscan" : { "minimum" : 0, "maximum" : 5.88235 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 18, + "hscan" : { "minimum" : 5.88235, "maximum" : 11.7647 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 19, + "hscan" : { "minimum" : 11.7647, "maximum" : 17.6471 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 20, + "hscan" : { "minimum" : 17.6471, "maximum" : 23.5294 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 21, + "hscan" : { "minimum" : 23.5294, "maximum" : 29.4118 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 22, + "hscan" : { "minimum" : 29.4118, "maximum" : 35.2941 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 23, + "hscan" : { "minimum" : 35.2941, "maximum" : 41.1765 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 24, + "hscan" : { "minimum" : 41.1765, "maximum" : 47.0588 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 25, + "hscan" : { "minimum" : 47.0588, "maximum" : 52.9412 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 26, + "hscan" : { "minimum" : 52.9412, "maximum" : 58.8235 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 27, + "hscan" : { "minimum" : 58.8235, "maximum" : 64.7059 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 28, + "hscan" : { "minimum" : 64.7059, "maximum" : 70.5882 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 29, + "hscan" : { "minimum" : 70.5882, "maximum" : 76.4706 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 30, + "hscan" : { "minimum" : 76.4706, "maximum" : 82.3529 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 31, + "hscan" : { "minimum" : 82.3529, "maximum" : 88.2353 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 32, + "hscan" : { "minimum" : 88.2353, "maximum" : 94.1176 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + // BOTTOM-RIGHT Corner + { + "index" : 33, + "hscan" : { "minimum" : 94.1176, "maximum" : 100 }, + "vscan" : { "minimum" : 90, "maximum" : 100 } + }, + { + "index" : 34, + "hscan" : { "minimum" : 90, "maximum" : 100 }, + "vscan" : { "minimum" : 80, "maximum" : 90 } + }, + { + "index" : 35, + "hscan" : { "minimum" : 90, "maximum" : 100 }, + "vscan" : { "minimum" : 70, "maximum" : 80 } + }, + { + "index" : 36, + "hscan" : { "minimum" : 90, "maximum" : 100 }, + "vscan" : { "minimum" : 60, "maximum" : 70 } + }, + { + "index" : 37, + "hscan" : { "minimum" : 90, "maximum" : 100 }, + "vscan" : { "minimum" : 50, "maximum" : 60 } + }, + { + "index" : 38, + "hscan" : { "minimum" : 90, "maximum" : 100 }, + "vscan" : { "minimum" : 40, "maximum" : 50 } + }, + { + "index" : 39, + "hscan" : { "minimum" : 90, "maximum" : 100 }, + "vscan" : { "minimum" : 30, "maximum" : 40 } + }, + { + "index" : 40, + "hscan" : { "minimum" : 90, "maximum" : 100 }, + "vscan" : { "minimum" : 20, "maximum" : 30 } + }, + { + "index" : 41, + "hscan" : { "minimum" : 90, "maximum" : 100 }, + "vscan" : { "minimum" : 10, "maximum" : 20 } + }, + // TOP-RIGHT Corner + { + "index" : 42, + "hscan" : { "minimum" : 94.1176, "maximum" : 100 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 43, + "hscan" : { "minimum" : 88.2353, "maximum" : 94.1176 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 44, + "hscan" : { "minimum" : 82.3529, "maximum" : 88.2353 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 45, + "hscan" : { "minimum" : 76.4706, "maximum" : 82.3529 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 46, + "hscan" : { "minimum" : 70.5882, "maximum" : 76.4706 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 47, + "hscan" : { "minimum" : 64.7059, "maximum" : 70.5882 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 48, + "hscan" : { "minimum" : 58.8235, "maximum" : 64.7059 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + }, + { + "index" : 49, + "hscan" : { "minimum" : 52.9412, "maximum" : 58.8235 }, + "vscan" : { "minimum" : 0, "maximum" : 10 } + } + ] +} diff --git a/config/Hyperion.schema.json b/config/Hyperion.schema.json new file mode 100644 index 00000000..2e9f6a78 --- /dev/null +++ b/config/Hyperion.schema.json @@ -0,0 +1,134 @@ +{ + "type":"object", + "required":true, + "properties":{ + "device": { + "type":"object", + "required":true, + "properties":{ + "name": { + "type":"string", + "required":true + }, + "type": { + "type":"string", + "required":true + }, + "output": { + "type":"string", + "required":true + }, + "interval": { + "type":"integer", + "required":true + }, + "rate": { + "type":"integer", + "required":true + } + }, + "additionalProperties": false + }, + "color": { + "type":"object", + "required":true, + "properties": { + "red": { + "type":"object", + "required":true, + "properties":{ + "gamma": { + "type":"number", + "required":true + }, + "adjust": { + "type":"number", + "required":true + }, + "blacklevel": { + "type":"number", + "required":true + } + } + }, + "green": { + "type":"object", + "required":true, + "properties":{ + "gamma": { + "type":"number", + "required":true + }, + "adjust": { + "type":"number", + "required":true + }, + "blacklevel": { + "type":"number", + "required":true + } + } + }, + "blue": { + "type":"object", + "required":true, + "properties":{ + "gamma": { + "type":"number", + "required":true + }, + "adjust": { + "type":"number", + "required":true + }, + "blacklevel": { + "type":"number", + "required":true + } + } + } + } + }, + "leds": { + "type":"array", + "required":true, + "items": { + "type":"object", + "properties": { + "index": { + "type":"integer", + "required":true + }, + "hscan": { + "type":"object", + "required":true, + "properties": { + "minimum": { + "type":"number", + "required":true + }, + "maximum": { + "type":"number", + "required":true + } + } + }, + "vscan": { + "type":"object", + "required":true, + "properties": { + "minimum": { + "type":"number", + "required":true + }, + "maximum": { + "type":"number", + "required":true + } + } + } + } + } + } + } +} diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt new file mode 100644 index 00000000..a7d9a073 --- /dev/null +++ b/dependencies/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2012 TNO, The Netherlands. +# +# This file contains information proprietary to TNO. +# +# Any disclosure or use of this information or any reproduction of this document or any part thereof for +# other than the specified purpose for which it is intended is expressly prohibited except as TNO may +# otherwise agree to in writing. + +add_subdirectory(build) diff --git a/dependencies/build/CMakeLists.txt b/dependencies/build/CMakeLists.txt new file mode 100644 index 00000000..58e98e7f --- /dev/null +++ b/dependencies/build/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2012 TNO, The Netherlands. +# +# This file contains information proprietary to TNO. +# +# Any disclosure or use of this information or any reproduction of this document or any part thereof for +# other than the specified purpose for which it is intended is expressly prohibited except as TNO may +# otherwise agree to in writing. + +add_subdirectory(jsoncpp) diff --git a/dependencies/build/jsoncpp/AUTHORS b/dependencies/build/jsoncpp/AUTHORS new file mode 100644 index 00000000..333e120d --- /dev/null +++ b/dependencies/build/jsoncpp/AUTHORS @@ -0,0 +1 @@ +Baptiste Lepilleur diff --git a/dependencies/build/jsoncpp/CMakeLists.txt b/dependencies/build/jsoncpp/CMakeLists.txt new file mode 100644 index 00000000..1b2f6197 --- /dev/null +++ b/dependencies/build/jsoncpp/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2012 TNO, The Netherlands. +# +# This file contains information proprietary to TNO. +# +# Any disclosure or use of this information or any reproduction of this document or any part thereof for +# other than the specified purpose for which it is intended is expressly prohibited except as TNO may +# otherwise agree to in writing. + +project(jsoncpp) + +# define the current source/header path +set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/dependencies/include/json) +set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/dependencies/build/jsoncpp) + +add_library(jsoncpp + ${CURRENT_HEADER_DIR}/autolink.h + ${CURRENT_HEADER_DIR}/config.h + ${CURRENT_HEADER_DIR}/features.h + ${CURRENT_HEADER_DIR}/forwards.h + ${CURRENT_HEADER_DIR}/json.h + ${CURRENT_HEADER_DIR}/reader.h + ${CURRENT_HEADER_DIR}/value.h + ${CURRENT_HEADER_DIR}/writer.h + + ${CURRENT_SOURCE_DIR}/json_batchallocator.h + ${CURRENT_SOURCE_DIR}/json_internalarray.inl + ${CURRENT_SOURCE_DIR}/json_internalmap.inl + ${CURRENT_SOURCE_DIR}/json_reader.cpp + ${CURRENT_SOURCE_DIR}/json_tool.h + ${CURRENT_SOURCE_DIR}/json_value.cpp + ${CURRENT_SOURCE_DIR}/json_valueiterator.inl + ${CURRENT_SOURCE_DIR}/json_writer.cpp) diff --git a/dependencies/build/jsoncpp/LICENSE b/dependencies/build/jsoncpp/LICENSE new file mode 100644 index 00000000..ca2bfe1a --- /dev/null +++ b/dependencies/build/jsoncpp/LICENSE @@ -0,0 +1,55 @@ +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +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. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. diff --git a/dependencies/build/jsoncpp/json_batchallocator.h b/dependencies/build/jsoncpp/json_batchallocator.h new file mode 100644 index 00000000..173e2ed2 --- /dev/null +++ b/dependencies/build/jsoncpp/json_batchallocator.h @@ -0,0 +1,130 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED +# define JSONCPP_BATCHALLOCATOR_H_INCLUDED + +# include +# include + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace Json { + +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated objects + * can be destroyed at once. The memory can be either released or reused for future + * allocation. + * + * The in-place new operator must be used to construct the object using the pointer + * returned by allocate. + */ +template +class BatchAllocator +{ +public: + typedef AllocatedType Type; + + BatchAllocator( unsigned int objectsPerPage = 255 ) + : freeHead_( 0 ) + , objectsPerPage_( objectsPerPage ) + { +// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); + assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. + assert( objectsPerPage >= 16 ); + batches_ = allocateBatch( 0 ); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() + { + for ( BatchInfo *batch = batches_; batch; ) + { + BatchInfo *nextBatch = batch->next_; + free( batch ); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects constructors. + AllocatedType *allocate() + { + if ( freeHead_ ) // returns node from free list. + { + AllocatedType *object = freeHead_; + freeHead_ = *(AllocatedType **)object; + return object; + } + if ( currentBatch_->used_ == currentBatch_->end_ ) + { + currentBatch_ = currentBatch_->next_; + while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) + currentBatch_ = currentBatch_->next_; + + if ( !currentBatch_ ) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch( objectsPerPage_ ); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType *allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the object. + void release( AllocatedType *object ) + { + assert( object != 0 ); + *(AllocatedType **)object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo + { + BatchInfo *next_; + AllocatedType *used_; + AllocatedType *end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator( const BatchAllocator & ); + void operator =( const BatchAllocator &); + + static BatchInfo *allocateBatch( unsigned int objectsPerPage ) + { + const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo *batch = static_cast( malloc( mallocSize ) ); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo *batches_; + BatchInfo *currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType *freeHead_; + unsigned int objectsPerPage_; +}; + + +} // namespace Json + +# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION + +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED + diff --git a/dependencies/build/jsoncpp/json_internalarray.inl b/dependencies/build/jsoncpp/json_internalarray.inl new file mode 100644 index 00000000..3a532ad7 --- /dev/null +++ b/dependencies/build/jsoncpp/json_internalarray.inl @@ -0,0 +1,456 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueArrayAllocator::~ValueArrayAllocator() +{ +} + +// ////////////////////////////////////////////////////////////////// +// class DefaultValueArrayAllocator +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destructArray( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + +#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + ValueInternalArray *array = arraysAllocator_.allocate(); + new (array) ValueInternalArray(); // placement new + return array; + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + ValueInternalArray *array = arraysAllocator_.allocate(); + new (array) ValueInternalArray( other ); // placement new + return array; + } + + virtual void destructArray( ValueInternalArray *array ) + { + if ( array ) + { + array->~ValueInternalArray(); + arraysAllocator_.release( array ); + } + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( pagesAllocator_.allocate() ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + pagesAllocator_.release( value ); + } +private: + BatchAllocator arraysAllocator_; + BatchAllocator pagesAllocator_; +}; +#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR + +static ValueArrayAllocator *&arrayAllocator() +{ + static DefaultValueArrayAllocator defaultAllocator; + static ValueArrayAllocator *arrayAllocator = &defaultAllocator; + return arrayAllocator; +} + +static struct DummyArrayAllocatorInitializer { + DummyArrayAllocatorInitializer() + { + arrayAllocator(); // ensure arrayAllocator() statics are initialized before main(). + } +} dummyArrayAllocatorInitializer; + +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +bool +ValueInternalArray::equals( const IteratorState &x, + const IteratorState &other ) +{ + return x.array_ == other.array_ + && x.currentItemIndex_ == other.currentItemIndex_ + && x.currentPageIndex_ == other.currentPageIndex_; +} + + +void +ValueInternalArray::increment( IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && + (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ + != it.array_->size_, + "ValueInternalArray::increment(): moving iterator beyond end" ); + ++(it.currentItemIndex_); + if ( it.currentItemIndex_ == itemsPerPage ) + { + it.currentItemIndex_ = 0; + ++(it.currentPageIndex_); + } +} + + +void +ValueInternalArray::decrement( IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_ + && it.currentItemIndex_ == 0, + "ValueInternalArray::decrement(): moving iterator beyond end" ); + if ( it.currentItemIndex_ == 0 ) + { + it.currentItemIndex_ = itemsPerPage-1; + --(it.currentPageIndex_); + } + else + { + --(it.currentItemIndex_); + } +} + + +Value & +ValueInternalArray::unsafeDereference( const IteratorState &it ) +{ + return (*(it.currentPageIndex_))[it.currentItemIndex_]; +} + + +Value & +ValueInternalArray::dereference( const IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && + (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ + < it.array_->size_, + "ValueInternalArray::dereference(): dereferencing invalid iterator" ); + return unsafeDereference( it ); +} + +void +ValueInternalArray::makeBeginIterator( IteratorState &it ) const +{ + it.array_ = const_cast( this ); + it.currentItemIndex_ = 0; + it.currentPageIndex_ = pages_; +} + + +void +ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const +{ + it.array_ = const_cast( this ); + it.currentItemIndex_ = index % itemsPerPage; + it.currentPageIndex_ = pages_ + index / itemsPerPage; +} + + +void +ValueInternalArray::makeEndIterator( IteratorState &it ) const +{ + makeIterator( it, size_ ); +} + + +ValueInternalArray::ValueInternalArray() + : pages_( 0 ) + , size_( 0 ) + , pageCount_( 0 ) +{ +} + + +ValueInternalArray::ValueInternalArray( const ValueInternalArray &other ) + : pages_( 0 ) + , pageCount_( 0 ) + , size_( other.size_ ) +{ + PageIndex minNewPages = other.size_ / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); + JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, + "ValueInternalArray::reserve(): bad reallocation" ); + IteratorState itOther; + other.makeBeginIterator( itOther ); + Value *value; + for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) ) + { + if ( index % itemsPerPage == 0 ) + { + PageIndex pageIndex = index / itemsPerPage; + value = arrayAllocator()->allocateArrayPage(); + pages_[pageIndex] = value; + } + new (value) Value( dereference( itOther ) ); + } +} + + +ValueInternalArray & +ValueInternalArray::operator =( const ValueInternalArray &other ) +{ + ValueInternalArray temp( other ); + swap( temp ); + return *this; +} + + +ValueInternalArray::~ValueInternalArray() +{ + // destroy all constructed items + IteratorState it; + IteratorState itEnd; + makeBeginIterator( it); + makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + value->~Value(); + } + // release all pages + PageIndex lastPageIndex = size_ / itemsPerPage; + for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) + arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); + // release pages index + arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ ); +} + + +void +ValueInternalArray::swap( ValueInternalArray &other ) +{ + Value **tempPages = pages_; + pages_ = other.pages_; + other.pages_ = tempPages; + ArrayIndex tempSize = size_; + size_ = other.size_; + other.size_ = tempSize; + PageIndex tempPageCount = pageCount_; + pageCount_ = other.pageCount_; + other.pageCount_ = tempPageCount; +} + +void +ValueInternalArray::clear() +{ + ValueInternalArray dummy; + swap( dummy ); +} + + +void +ValueInternalArray::resize( ArrayIndex newSize ) +{ + if ( newSize == 0 ) + clear(); + else if ( newSize < size_ ) + { + IteratorState it; + IteratorState itEnd; + makeIterator( it, newSize ); + makeIterator( itEnd, size_ ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + value->~Value(); + } + PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; + PageIndex lastPageIndex = size_ / itemsPerPage; + for ( ; pageIndex < lastPageIndex; ++pageIndex ) + arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); + size_ = newSize; + } + else if ( newSize > size_ ) + resolveReference( newSize ); +} + + +void +ValueInternalArray::makeIndexValid( ArrayIndex index ) +{ + // Need to enlarge page index ? + if ( index >= pageCount_ * itemsPerPage ) + { + PageIndex minNewPages = (index + 1) / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); + JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); + } + + // Need to allocate new pages ? + ArrayIndex nextPageIndex = + (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage + : size_; + if ( nextPageIndex <= index ) + { + PageIndex pageIndex = nextPageIndex / itemsPerPage; + PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; + for ( ; pageToAllocate-- > 0; ++pageIndex ) + pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); + } + + // Initialize all new entries + IteratorState it; + IteratorState itEnd; + makeIterator( it, size_ ); + size_ = index + 1; + makeIterator( itEnd, size_ ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + new (value) Value(); // Construct a default value using placement new + } +} + +Value & +ValueInternalArray::resolveReference( ArrayIndex index ) +{ + if ( index >= size_ ) + makeIndexValid( index ); + return pages_[index/itemsPerPage][index%itemsPerPage]; +} + +Value * +ValueInternalArray::find( ArrayIndex index ) const +{ + if ( index >= size_ ) + return 0; + return &(pages_[index/itemsPerPage][index%itemsPerPage]); +} + +ValueInternalArray::ArrayIndex +ValueInternalArray::size() const +{ + return size_; +} + +int +ValueInternalArray::distance( const IteratorState &x, const IteratorState &y ) +{ + return indexOf(y) - indexOf(x); +} + + +ValueInternalArray::ArrayIndex +ValueInternalArray::indexOf( const IteratorState &iterator ) +{ + if ( !iterator.array_ ) + return ArrayIndex(-1); + return ArrayIndex( + (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage + + iterator.currentItemIndex_ ); +} + + +int +ValueInternalArray::compare( const ValueInternalArray &other ) const +{ + int sizeDiff( size_ - other.size_ ); + if ( sizeDiff != 0 ) + return sizeDiff; + + for ( ArrayIndex index =0; index < size_; ++index ) + { + int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( + other.pages_[index/itemsPerPage][index%itemsPerPage] ); + if ( diff != 0 ) + return diff; + } + return 0; +} + +} // namespace Json diff --git a/dependencies/build/jsoncpp/json_internalmap.inl b/dependencies/build/jsoncpp/json_internalmap.inl new file mode 100644 index 00000000..f2fa1606 --- /dev/null +++ b/dependencies/build/jsoncpp/json_internalmap.inl @@ -0,0 +1,615 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalMap +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); + * This optimization is used by the fast allocator. + */ +ValueInternalLink::ValueInternalLink() + : previous_( 0 ) + , next_( 0 ) +{ +} + +ValueInternalLink::~ValueInternalLink() +{ + for ( int index =0; index < itemPerLink; ++index ) + { + if ( !items_[index].isItemAvailable() ) + { + if ( !items_[index].isMemberNameStatic() ) + free( keys_[index] ); + } + else + break; + } +} + + + +ValueMapAllocator::~ValueMapAllocator() +{ +} + +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueMapAllocator : public ValueMapAllocator +{ +public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } +}; +#else +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueMapAllocator : public ValueMapAllocator +{ +public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + ValueInternalMap *map = mapsAllocator_.allocate(); + new (map) ValueInternalMap(); // placement new + return map; + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + ValueInternalMap *map = mapsAllocator_.allocate(); + new (map) ValueInternalMap( other ); // placement new + return map; + } + + virtual void destructMap( ValueInternalMap *map ) + { + if ( map ) + { + map->~ValueInternalMap(); + mapsAllocator_.release( map ); + } + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + ValueInternalLink *link = linksAllocator_.allocate(); + memset( link, 0, sizeof(ValueInternalLink) ); + return link; + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + link->~ValueInternalLink(); + linksAllocator_.release( link ); + } +private: + BatchAllocator mapsAllocator_; + BatchAllocator linksAllocator_; +}; +#endif + +static ValueMapAllocator *&mapAllocator() +{ + static DefaultValueMapAllocator defaultAllocator; + static ValueMapAllocator *mapAllocator = &defaultAllocator; + return mapAllocator; +} + +static struct DummyMapAllocatorInitializer { + DummyMapAllocatorInitializer() + { + mapAllocator(); // ensure mapAllocator() statics are initialized before main(). + } +} dummyMapAllocatorInitializer; + + + +// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. + +/* +use linked list hash map. +buckets array is a container. +linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) +value have extra state: valid, available, deleted +*/ + + +ValueInternalMap::ValueInternalMap() + : buckets_( 0 ) + , tailLink_( 0 ) + , bucketsSize_( 0 ) + , itemCount_( 0 ) +{ +} + + +ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) + : buckets_( 0 ) + , tailLink_( 0 ) + , bucketsSize_( 0 ) + , itemCount_( 0 ) +{ + reserve( other.itemCount_ ); + IteratorState it; + IteratorState itEnd; + other.makeBeginIterator( it ); + other.makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + bool isStatic; + const char *memberName = key( it, isStatic ); + const Value &aValue = value( it ); + resolveReference(memberName, isStatic) = aValue; + } +} + + +ValueInternalMap & +ValueInternalMap::operator =( const ValueInternalMap &other ) +{ + ValueInternalMap dummy( other ); + swap( dummy ); + return *this; +} + + +ValueInternalMap::~ValueInternalMap() +{ + if ( buckets_ ) + { + for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) + { + ValueInternalLink *link = buckets_[bucketIndex].next_; + while ( link ) + { + ValueInternalLink *linkToRelease = link; + link = link->next_; + mapAllocator()->releaseMapLink( linkToRelease ); + } + } + mapAllocator()->releaseMapBuckets( buckets_ ); + } +} + + +void +ValueInternalMap::swap( ValueInternalMap &other ) +{ + ValueInternalLink *tempBuckets = buckets_; + buckets_ = other.buckets_; + other.buckets_ = tempBuckets; + ValueInternalLink *tempTailLink = tailLink_; + tailLink_ = other.tailLink_; + other.tailLink_ = tempTailLink; + BucketIndex tempBucketsSize = bucketsSize_; + bucketsSize_ = other.bucketsSize_; + other.bucketsSize_ = tempBucketsSize; + BucketIndex tempItemCount = itemCount_; + itemCount_ = other.itemCount_; + other.itemCount_ = tempItemCount; +} + + +void +ValueInternalMap::clear() +{ + ValueInternalMap dummy; + swap( dummy ); +} + + +ValueInternalMap::BucketIndex +ValueInternalMap::size() const +{ + return itemCount_; +} + +bool +ValueInternalMap::reserveDelta( BucketIndex growth ) +{ + return reserve( itemCount_ + growth ); +} + +bool +ValueInternalMap::reserve( BucketIndex newItemCount ) +{ + if ( !buckets_ && newItemCount > 0 ) + { + buckets_ = mapAllocator()->allocateMapBuckets( 1 ); + bucketsSize_ = 1; + tailLink_ = &buckets_[0]; + } +// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; + return true; +} + + +const Value * +ValueInternalMap::find( const char *key ) const +{ + if ( !bucketsSize_ ) + return 0; + HashKey hashedKey = hash( key ); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for ( const ValueInternalLink *current = &buckets_[bucketIndex]; + current != 0; + current = current->next_ ) + { + for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( current->items_[index].isItemAvailable() ) + return 0; + if ( strcmp( key, current->keys_[index] ) == 0 ) + return ¤t->items_[index]; + } + } + return 0; +} + + +Value * +ValueInternalMap::find( const char *key ) +{ + const ValueInternalMap *constThis = this; + return const_cast( constThis->find( key ) ); +} + + +Value & +ValueInternalMap::resolveReference( const char *key, + bool isStatic ) +{ + HashKey hashedKey = hash( key ); + if ( bucketsSize_ ) + { + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink **previous = 0; + BucketIndex index; + for ( ValueInternalLink *current = &buckets_[bucketIndex]; + current != 0; + previous = ¤t->next_, current = current->next_ ) + { + for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( current->items_[index].isItemAvailable() ) + return setNewItem( key, isStatic, current, index ); + if ( strcmp( key, current->keys_[index] ) == 0 ) + return current->items_[index]; + } + } + } + + reserveDelta( 1 ); + return unsafeAdd( key, isStatic, hashedKey ); +} + + +void +ValueInternalMap::remove( const char *key ) +{ + HashKey hashedKey = hash( key ); + if ( !bucketsSize_ ) + return; + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for ( ValueInternalLink *link = &buckets_[bucketIndex]; + link != 0; + link = link->next_ ) + { + BucketIndex index; + for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( link->items_[index].isItemAvailable() ) + return; + if ( strcmp( key, link->keys_[index] ) == 0 ) + { + doActualRemove( link, index, bucketIndex ); + return; + } + } + } +} + +void +ValueInternalMap::doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ) +{ + // find last item of the bucket and swap it with the 'removed' one. + // set removed items flags to 'available'. + // if last page only contains 'available' items, then desallocate it (it's empty) + ValueInternalLink *&lastLink = getLastLinkInBucket( index ); + BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 + for ( ; + lastItemIndex < ValueInternalLink::itemPerLink; + ++lastItemIndex ) // may be optimized with dicotomic search + { + if ( lastLink->items_[lastItemIndex].isItemAvailable() ) + break; + } + + BucketIndex lastUsedIndex = lastItemIndex - 1; + Value *valueToDelete = &link->items_[index]; + Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; + if ( valueToDelete != valueToPreserve ) + valueToDelete->swap( *valueToPreserve ); + if ( lastUsedIndex == 0 ) // page is now empty + { // remove it from bucket linked list and delete it. + ValueInternalLink *linkPreviousToLast = lastLink->previous_; + if ( linkPreviousToLast != 0 ) // can not deleted bucket link. + { + mapAllocator()->releaseMapLink( lastLink ); + linkPreviousToLast->next_ = 0; + lastLink = linkPreviousToLast; + } + } + else + { + Value dummy; + valueToPreserve->swap( dummy ); // restore deleted to default Value. + valueToPreserve->setItemUsed( false ); + } + --itemCount_; +} + + +ValueInternalLink *& +ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) +{ + if ( bucketIndex == bucketsSize_ - 1 ) + return tailLink_; + ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; + if ( !previous ) + previous = &buckets_[bucketIndex]; + return previous; +} + + +Value & +ValueInternalMap::setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ) +{ + char *duplicatedKey = makeMemberName( key ); + ++itemCount_; + link->keys_[index] = duplicatedKey; + link->items_[index].setItemUsed(); + link->items_[index].setMemberNameIsStatic( isStatic ); + return link->items_[index]; // items already default constructed. +} + + +Value & +ValueInternalMap::unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ) +{ + JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); + ValueInternalLink *link = previousLink; + BucketIndex index; + for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( link->items_[index].isItemAvailable() ) + break; + } + if ( index == ValueInternalLink::itemPerLink ) // need to add a new page + { + ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); + index = 0; + link->next_ = newLink; + previousLink = newLink; + link = newLink; + } + return setNewItem( key, isStatic, link, index ); +} + + +ValueInternalMap::HashKey +ValueInternalMap::hash( const char *key ) const +{ + HashKey hash = 0; + while ( *key ) + hash += *key++ * 37; + return hash; +} + + +int +ValueInternalMap::compare( const ValueInternalMap &other ) const +{ + int sizeDiff( itemCount_ - other.itemCount_ ); + if ( sizeDiff != 0 ) + return sizeDiff; + // Strict order guaranty is required. Compare all keys FIRST, then compare values. + IteratorState it; + IteratorState itEnd; + makeBeginIterator( it ); + makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + if ( !other.find( key( it ) ) ) + return 1; + } + + // All keys are equals, let's compare values + makeBeginIterator( it ); + for ( ; !equals(it,itEnd); increment(it) ) + { + const Value *otherValue = other.find( key( it ) ); + int valueDiff = value(it).compare( *otherValue ); + if ( valueDiff != 0 ) + return valueDiff; + } + return 0; +} + + +void +ValueInternalMap::makeBeginIterator( IteratorState &it ) const +{ + it.map_ = const_cast( this ); + it.bucketIndex_ = 0; + it.itemIndex_ = 0; + it.link_ = buckets_; +} + + +void +ValueInternalMap::makeEndIterator( IteratorState &it ) const +{ + it.map_ = const_cast( this ); + it.bucketIndex_ = bucketsSize_; + it.itemIndex_ = 0; + it.link_ = 0; +} + + +bool +ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) +{ + return x.map_ == other.map_ + && x.bucketIndex_ == other.bucketIndex_ + && x.link_ == other.link_ + && x.itemIndex_ == other.itemIndex_; +} + + +void +ValueInternalMap::incrementBucket( IteratorState &iterator ) +{ + ++iterator.bucketIndex_; + JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, + "ValueInternalMap::increment(): attempting to iterate beyond end." ); + if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) + iterator.link_ = 0; + else + iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); + iterator.itemIndex_ = 0; +} + + +void +ValueInternalMap::increment( IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); + ++iterator.itemIndex_; + if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) + { + JSON_ASSERT_MESSAGE( iterator.link_ != 0, + "ValueInternalMap::increment(): attempting to iterate beyond end." ); + iterator.link_ = iterator.link_->next_; + if ( iterator.link_ == 0 ) + incrementBucket( iterator ); + } + else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) + { + incrementBucket( iterator ); + } +} + + +void +ValueInternalMap::decrement( IteratorState &iterator ) +{ + if ( iterator.itemIndex_ == 0 ) + { + JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); + if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) + { + JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); + --(iterator.bucketIndex_); + } + iterator.link_ = iterator.link_->previous_; + iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; + } +} + + +const char * +ValueInternalMap::key( const IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + return iterator.link_->keys_[iterator.itemIndex_]; +} + +const char * +ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); + return iterator.link_->keys_[iterator.itemIndex_]; +} + + +Value & +ValueInternalMap::value( const IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + return iterator.link_->items_[iterator.itemIndex_]; +} + + +int +ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) +{ + int offset = 0; + IteratorState it = x; + while ( !equals( it, y ) ) + increment( it ); + return offset; +} + +} // namespace Json diff --git a/dependencies/build/jsoncpp/json_reader.cpp b/dependencies/build/jsoncpp/json_reader.cpp new file mode 100644 index 00000000..8bb03042 --- /dev/null +++ b/dependencies/build/jsoncpp/json_reader.cpp @@ -0,0 +1,880 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include +# include +# include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_( true ) + , strictRoot_( false ) +{ +} + + +Features +Features::all() +{ + return Features(); +} + + +Features +Features::strictMode() +{ + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + + +static bool +containsNewLine( Reader::Location begin, + Reader::Location end ) +{ + for ( ;begin < end; ++begin ) + if ( *begin == '\n' || *begin == '\r' ) + return true; + return false; +} + + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : features_( Features::all() ) +{ +} + + +Reader::Reader( const Features &features ) + : features_( features ) +{ +} + + +bool +Reader::parse( const std::string &document, + Value &root, + bool collectComments ) +{ + document_ = document; + const char *begin = document_.c_str(); + const char *end = begin + document_.length(); + return parse( begin, end, root, collectComments ); +} + + +bool +Reader::parse( std::istream& sin, + Value &root, + bool collectComments ) +{ + //std::istream_iterator begin(sin); + //std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse( doc, root, collectComments ); +} + +bool +Reader::parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments ) +{ + if ( !features_.allowComments_ ) + { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while ( !nodes_.empty() ) + nodes_.pop(); + nodes_.push( &root ); + + bool successful = readValue(); + Token token; + skipCommentTokens( token ); + if ( collectComments_ && !commentsBefore_.empty() ) + root.setComment( commentsBefore_, commentAfter ); + if ( features_.strictRoot_ ) + { + if ( !root.isArray() && !root.isObject() ) + { + // Set error location to start of doc, ideally should be first token found in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( "A valid JSON document must be either an array or an object value.", + token ); + return false; + } + } + return successful; +} + + +bool +Reader::readValue() +{ + Token token; + skipCommentTokens( token ); + bool successful = true; + + if ( collectComments_ && !commentsBefore_.empty() ) + { + currentValue().setComment( commentsBefore_, commentBefore ); + commentsBefore_ = ""; + } + + + switch ( token.type_ ) + { + case tokenObjectBegin: + successful = readObject( token ); + break; + case tokenArrayBegin: + successful = readArray( token ); + break; + case tokenNumber: + successful = decodeNumber( token ); + break; + case tokenString: + successful = decodeString( token ); + break; + case tokenTrue: + currentValue() = true; + break; + case tokenFalse: + currentValue() = false; + break; + case tokenNull: + currentValue() = Value(); + break; + default: + return addError( "Syntax error: value, object or array expected.", token ); + } + + if ( collectComments_ ) + { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + + +void +Reader::skipCommentTokens( Token &token ) +{ + if ( features_.allowComments_ ) + { + do + { + readToken( token ); + } + while ( token.type_ == tokenComment ); + } + else + { + readToken( token ); + } +} + + +bool +Reader::expectToken( TokenType type, Token &token, const char *message ) +{ + readToken( token ); + if ( token.type_ != type ) + return addError( message, token ); + return true; +} + + +bool +Reader::readToken( Token &token ) +{ + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch ( c ) + { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match( "rue", 3 ); + break; + case 'f': + token.type_ = tokenFalse; + ok = match( "alse", 4 ); + break; + case 'n': + token.type_ = tokenNull; + ok = match( "ull", 3 ); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if ( !ok ) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + + +void +Reader::skipSpaces() +{ + while ( current_ != end_ ) + { + Char c = *current_; + if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) + ++current_; + else + break; + } +} + + +bool +Reader::match( Location pattern, + int patternLength ) +{ + if ( end_ - current_ < patternLength ) + return false; + int index = patternLength; + while ( index-- ) + if ( current_[index] != pattern[index] ) + return false; + current_ += patternLength; + return true; +} + + +bool +Reader::readComment() +{ + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if ( c == '*' ) + successful = readCStyleComment(); + else if ( c == '/' ) + successful = readCppStyleComment(); + if ( !successful ) + return false; + + if ( collectComments_ ) + { + CommentPlacement placement = commentBefore; + if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) + { + if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) + placement = commentAfterOnSameLine; + } + + addComment( commentBegin, current_, placement ); + } + return true; +} + + +void +Reader::addComment( Location begin, + Location end, + CommentPlacement placement ) +{ + assert( collectComments_ ); + if ( placement == commentAfterOnSameLine ) + { + assert( lastValue_ != 0 ); + lastValue_->setComment( std::string( begin, end ), placement ); + } + else + { + if ( !commentsBefore_.empty() ) + commentsBefore_ += "\n"; + commentsBefore_ += std::string( begin, end ); + } +} + + +bool +Reader::readCStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '*' && *current_ == '/' ) + break; + } + return getNextChar() == '/'; +} + + +bool +Reader::readCppStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '\r' || c == '\n' ) + break; + } + return true; +} + + +void +Reader::readNumber() +{ + while ( current_ != end_ ) + { + if ( !(*current_ >= '0' && *current_ <= '9') && + !in( *current_, '.', 'e', 'E', '+', '-' ) ) + break; + ++current_; + } +} + +bool +Reader::readString() +{ + Char c = 0; + while ( current_ != end_ ) + { + c = getNextChar(); + if ( c == '\\' ) + getNextChar(); + else if ( c == '"' ) + break; + } + return c == '"'; +} + + +bool +Reader::readObject( Token &/*tokenStart*/ ) +{ + Token tokenName; + std::string name; + currentValue() = Value( objectValue ); + while ( readToken( tokenName ) ) + { + bool initialTokenOk = true; + while ( tokenName.type_ == tokenComment && initialTokenOk ) + initialTokenOk = readToken( tokenName ); + if ( !initialTokenOk ) + break; + if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object + return true; + if ( tokenName.type_ != tokenString ) + break; + + name = ""; + if ( !decodeString( tokenName, name ) ) + return recoverFromError( tokenObjectEnd ); + + Token colon; + if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) + { + return addErrorAndRecover( "Missing ':' after object member name", + colon, + tokenObjectEnd ); + } + Value &value = currentValue()[ name ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenObjectEnd ); + + Token comma; + if ( !readToken( comma ) + || ( comma.type_ != tokenObjectEnd && + comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment ) ) + { + return addErrorAndRecover( "Missing ',' or '}' in object declaration", + comma, + tokenObjectEnd ); + } + bool finalizeTokenOk = true; + while ( comma.type_ == tokenComment && + finalizeTokenOk ) + finalizeTokenOk = readToken( comma ); + if ( comma.type_ == tokenObjectEnd ) + return true; + } + return addErrorAndRecover( "Missing '}' or object member name", + tokenName, + tokenObjectEnd ); +} + + +bool +Reader::readArray( Token &/*tokenStart*/ ) +{ + currentValue() = Value( arrayValue ); + skipSpaces(); + if ( *current_ == ']' ) // empty array + { + Token endArray; + readToken( endArray ); + return true; + } + int index = 0; + for (;;) + { + Value &value = currentValue()[ index++ ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenArrayEnd ); + + Token token; + // Accept Comment after last item in the array. + ok = readToken( token ); + while ( token.type_ == tokenComment && ok ) + { + ok = readToken( token ); + } + bool badTokenType = ( token.type_ != tokenArraySeparator && + token.type_ != tokenArrayEnd ); + if ( !ok || badTokenType ) + { + return addErrorAndRecover( "Missing ',' or ']' in array declaration", + token, + tokenArrayEnd ); + } + if ( token.type_ == tokenArrayEnd ) + break; + } + return true; +} + + +bool +Reader::decodeNumber( Token &token ) +{ + bool isDouble = false; + for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) + { + isDouble = isDouble + || in( *inspect, '.', 'e', 'E', '+' ) + || ( *inspect == '-' && inspect != token.start_ ); + } + if ( isDouble ) + return decodeDouble( token ); + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if ( isNegative ) + ++current; + Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::UInt lastDigitThreshold = Value::UInt( maxIntegerValue % 10 ); + assert( lastDigitThreshold >=0 && lastDigitThreshold <= 9 ); + Value::LargestUInt value = 0; + while ( current < token.end_ ) + { + Char c = *current++; + if ( c < '0' || c > '9' ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + Value::UInt digit(c - '0'); + if ( value >= threshold ) + { + // If the current digit is not the last one, or if it is + // greater than the last digit of the maximum integer value, + // the parse the number as a double. + if ( current != token.end_ || digit > lastDigitThreshold ) + { + return decodeDouble( token ); + } + } + value = value * 10 + digit; + } + if ( isNegative ) + currentValue() = -Value::LargestInt( value ); + else if ( value <= Value::LargestUInt(Value::maxInt) ) + currentValue() = Value::LargestInt( value ); + else + currentValue() = value; + return true; +} + + +bool +Reader::decodeDouble( Token &token ) +{ + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + if ( length <= bufferSize ) + { + Char buffer[bufferSize+1]; + memcpy( buffer, token.start_, length ); + buffer[length] = 0; + count = sscanf( buffer, "%lf", &value ); + } + else + { + std::string buffer( token.start_, token.end_ ); + count = sscanf( buffer.c_str(), "%lf", &value ); + } + + if ( count != 1 ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + currentValue() = value; + return true; +} + + +bool +Reader::decodeString( Token &token ) +{ + std::string decoded; + if ( !decodeString( token, decoded ) ) + return false; + currentValue() = decoded; + return true; +} + + +bool +Reader::decodeString( Token &token, std::string &decoded ) +{ + decoded.reserve( token.end_ - token.start_ - 2 ); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while ( current != end ) + { + Char c = *current++; + if ( c == '"' ) + break; + else if ( c == '\\' ) + { + if ( current == end ) + return addError( "Empty escape sequence in string", token, current ); + Char escape = *current++; + switch ( escape ) + { + case '"': decoded += '"'; break; + case '/': decoded += '/'; break; + case '\\': decoded += '\\'; break; + case 'b': decoded += '\b'; break; + case 'f': decoded += '\f'; break; + case 'n': decoded += '\n'; break; + case 'r': decoded += '\r'; break; + case 't': decoded += '\t'; break; + case 'u': + { + unsigned int unicode; + if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) + return false; + decoded += codePointToUTF8(unicode); + } + break; + default: + return addError( "Bad escape sequence in string", token, current ); + } + } + else + { + decoded += c; + } + } + return true; +} + +bool +Reader::decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + + if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) + { + // surrogate pairs + if (end - current < 6) + return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++)== 'u') + { + if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) + { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } + else + return false; + } + else + return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); + } + return true; +} + +bool +Reader::decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + if ( end - current < 4 ) + return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); + unicode = 0; + for ( int index =0; index < 4; ++index ) + { + Char c = *current++; + unicode *= 16; + if ( c >= '0' && c <= '9' ) + unicode += c - '0'; + else if ( c >= 'a' && c <= 'f' ) + unicode += c - 'a' + 10; + else if ( c >= 'A' && c <= 'F' ) + unicode += c - 'A' + 10; + else + return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); + } + return true; +} + + +bool +Reader::addError( const std::string &message, + Token &token, + Location extra ) +{ + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back( info ); + return false; +} + + +bool +Reader::recoverFromError( TokenType skipUntilToken ) +{ + int errorCount = int(errors_.size()); + Token skip; + for (;;) + { + if ( !readToken(skip) ) + errors_.resize( errorCount ); // discard errors caused by recovery + if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) + break; + } + errors_.resize( errorCount ); + return false; +} + + +bool +Reader::addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ) +{ + addError( message, token ); + return recoverFromError( skipUntilToken ); +} + + +Value & +Reader::currentValue() +{ + return *(nodes_.top()); +} + + +Reader::Char +Reader::getNextChar() +{ + if ( current_ == end_ ) + return 0; + return *current_++; +} + + +void +Reader::getLocationLineAndColumn( Location location, + int &line, + int &column ) const +{ + Location current = begin_; + Location lastLineStart = current; + line = 0; + while ( current < location && current != end_ ) + { + Char c = *current++; + if ( c == '\r' ) + { + if ( *current == '\n' ) + ++current; + lastLineStart = current; + ++line; + } + else if ( c == '\n' ) + { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + + +std::string +Reader::getLocationLineAndColumn( Location location ) const +{ + int line, column; + getLocationLineAndColumn( location, line, column ); + char buffer[18+16+16+1]; + sprintf( buffer, "Line %d, Column %d", line, column ); + return buffer; +} + + +// Deprecated. Preserved for backward compatibility +std::string +Reader::getFormatedErrorMessages() const +{ + return getFormattedErrorMessages(); +} + + +std::string +Reader::getFormattedErrorMessages() const +{ + std::string formattedMessage; + for ( Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError ) + { + const ErrorInfo &error = *itError; + formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if ( error.extra_ ) + formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; + } + return formattedMessage; +} + + +std::istream& operator>>( std::istream &sin, Value &root ) +{ + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + //JSON_ASSERT( ok ); + if (!ok) throw std::runtime_error(reader.getFormattedErrorMessages()); + return sin; +} + + +} // namespace Json diff --git a/dependencies/build/jsoncpp/json_tool.h b/dependencies/build/jsoncpp/json_tool.h new file mode 100644 index 00000000..658031bb --- /dev/null +++ b/dependencies/build/jsoncpp/json_tool.h @@ -0,0 +1,93 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +# define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { + +/// Converts a unicode code-point to UTF-8. +static inline std::string +codePointToUTF8(unsigned int cp) +{ + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) + { + result.resize(1); + result[0] = static_cast(cp); + } + else if (cp <= 0x7FF) + { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } + else if (cp <= 0xFFFF) + { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } + else if (cp <= 0x10FFFF) + { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + + +/// Returns true if ch is a control character (in range [0,32[). +static inline bool +isControlCharacter(char ch) +{ + return ch > 0 && ch <= 0x1F; +} + + +enum { + /// Constant that specify the size of the buffer that must be passed to uintToString. + uintToStringBufferSize = 3*sizeof(LargestUInt)+1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void +uintToString( LargestUInt value, + char *¤t ) +{ + *--current = 0; + do + { + *--current = char(value % 10) + '0'; + value /= 10; + } + while ( value != 0 ); +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED diff --git a/dependencies/build/jsoncpp/json_value.cpp b/dependencies/build/jsoncpp/json_value.cpp new file mode 100644 index 00000000..ff98f635 --- /dev/null +++ b/dependencies/build/jsoncpp/json_value.cpp @@ -0,0 +1,1829 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include +# include +# ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +# include "json_batchallocator.h" +# endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +# include +#endif +#include // size_t + +#define JSON_ASSERT_UNREACHABLE assert( false ) +#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw +#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message ); +#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) JSON_FAIL_MESSAGE( message ) + +namespace Json { + +const Value Value::null; +const Int Value::minInt = Int( ~(UInt(-1)/2) ); +const Int Value::maxInt = Int( UInt(-1)/2 ); +const UInt Value::maxUInt = UInt(-1); +const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) ); +const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 ); +const UInt64 Value::maxUInt64 = UInt64(-1); +const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) ); +const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 ); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + + +/// Unknown size marker +static const unsigned int unknown = (unsigned)-1; + + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char * +duplicateStringValue( const char *value, + unsigned int length = unknown ) +{ + if ( length == unknown ) + length = (unsigned int)strlen(value); + char *newString = static_cast( malloc( length + 1 ) ); + JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" ); + memcpy( newString, value, length ); + newString[length] = 0; + return newString; +} + + +/** Free the string duplicated by duplicateStringValue(). + */ +static inline void +releaseStringValue( char *value ) +{ + if ( value ) + free( value ); +} + +} // namespace Json + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +# include "json_internalarray.inl" +# include "json_internalmap.inl" +# endif // JSON_VALUE_USE_INTERNAL_MAP + +# include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + + +Value::CommentInfo::CommentInfo() + : comment_( 0 ) +{ +} + +Value::CommentInfo::~CommentInfo() +{ + if ( comment_ ) + releaseStringValue( comment_ ); +} + + +void +Value::CommentInfo::setComment( const char *text ) +{ + if ( comment_ ) + releaseStringValue( comment_ ); + JSON_ASSERT( text != 0 ); + JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue( text ); +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +# ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString( ArrayIndex index ) + : cstr_( 0 ) + , index_( index ) +{ +} + +Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) + : cstr_( allocate == duplicate ? duplicateStringValue(cstr) + : cstr ) + , index_( allocate ) +{ +} + +Value::CZString::CZString( const CZString &other ) +: cstr_( other.index_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue( other.cstr_ ) + : other.cstr_ ) + , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) + : other.index_ ) +{ +} + +Value::CZString::~CZString() +{ + if ( cstr_ && index_ == duplicate ) + releaseStringValue( const_cast( cstr_ ) ); +} + +void +Value::CZString::swap( CZString &other ) +{ + std::swap( cstr_, other.cstr_ ); + std::swap( index_, other.index_ ); +} + +Value::CZString & +Value::CZString::operator =( const CZString &other ) +{ + CZString temp( other ); + swap( temp ); + return *this; +} + +bool +Value::CZString::operator<( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) < 0; + return index_ < other.index_; +} + +bool +Value::CZString::operator==( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) == 0; + return index_ == other.index_; +} + + +ArrayIndex +Value::CZString::index() const +{ + return index_; +} + + +const char * +Value::CZString::c_str() const +{ + return cstr_; +} + +bool +Value::CZString::isStaticString() const +{ + return index_ == noDuplication; +} + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value( ValueType type ) + : type_( type ) + , allocated_( 0 ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + switch ( type ) + { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + + +#if defined(JSON_HAS_INT64) +Value::Value( UInt value ) + : type_( uintValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.uint_ = value; +} + +Value::Value( Int value ) + : type_( intValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.int_ = value; +} + +#endif // if defined(JSON_HAS_INT64) + + +Value::Value( Int64 value ) + : type_( intValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.int_ = value; +} + + +Value::Value( UInt64 value ) + : type_( uintValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.uint_ = value; +} + +Value::Value( double value ) + : type_( realValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.real_ = value; +} + +Value::Value( const char *value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = duplicateStringValue( value ); +} + + +Value::Value( const char *beginValue, + const char *endValue ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = duplicateStringValue( beginValue, + (unsigned int)(endValue - beginValue) ); +} + + +Value::Value( const std::string &value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = duplicateStringValue( value.c_str(), + (unsigned int)value.length() ); + +} + +Value::Value( const StaticString &value ) + : type_( stringValue ) + , allocated_( false ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = const_cast( value.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +Value::Value( const CppTL::ConstString &value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = duplicateStringValue( value, value.length() ); +} +# endif + +Value::Value( bool value ) + : type_( booleanValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.bool_ = value; +} + + +Value::Value( const Value &other ) + : type_( other.type_ ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if ( other.value_.string_ ) + { + value_.string_ = duplicateStringValue( other.value_.string_ ); + allocated_ = true; + } + else + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues( *other.value_.map_ ); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + if ( other.comments_ ) + { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) + { + const CommentInfo &otherComment = other.comments_[comment]; + if ( otherComment.comment_ ) + comments_[comment].setComment( otherComment.comment_ ); + } + } +} + + +Value::~Value() +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if ( allocated_ ) + releaseStringValue( value_.string_ ); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray( value_.array_ ); + break; + case objectValue: + mapAllocator()->destructMap( value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if ( comments_ ) + delete[] comments_; +} + +Value & +Value::operator=( const Value &other ) +{ + Value temp( other ); + swap( temp ); + return *this; +} + +void +Value::swap( Value &other ) +{ + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap( value_, other.value_ ); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; +} + +ValueType +Value::type() const +{ + return type_; +} + + +int +Value::compare( const Value &other ) const +{ + if ( *this < other ) + return -1; + if ( *this > other ) + return 1; + return 0; +} + + +bool +Value::operator <( const Value &other ) const +{ + int typeDelta = type_ - other.type_; + if ( typeDelta ) + return typeDelta < 0 ? true : false; + switch ( type_ ) + { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + return ( value_.string_ == 0 && other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) < 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + { + int delta = int( value_.map_->size() - other.value_.map_->size() ); + if ( delta ) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) < 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) < 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool +Value::operator <=( const Value &other ) const +{ + return !(other < *this); +} + +bool +Value::operator >=( const Value &other ) const +{ + return !(*this < other); +} + +bool +Value::operator >( const Value &other ) const +{ + return other < *this; +} + +bool +Value::operator ==( const Value &other ) const +{ + //if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if ( type_ != temp ) + return false; + switch ( type_ ) + { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + return ( value_.string_ == other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) == 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() + && (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) == 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) == 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool +Value::operator !=( const Value &other ) const +{ + return !( *this == other ); +} + +const char * +Value::asCString() const +{ + JSON_ASSERT( type_ == stringValue ); + return value_.string_; +} + + +std::string +Value::asString() const +{ + switch ( type_ ) + { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + case uintValue: + case realValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE( "Type is not convertible to string" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return ""; // unreachable +} + +# ifdef JSON_USE_CPPTL +CppTL::ConstString +Value::asConstString() const +{ + return CppTL::ConstString( asString().c_str() ); +} +# endif + + +Value::Int +Value::asInt() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE( value_.int_ >= minInt && value_.int_ <= maxInt, "unsigned integer out of signed int range" ); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE( value_.uint_ <= UInt(maxInt), "unsigned integer out of signed int range" ); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); + return Int( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE( "Type is not convertible to int" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + + +Value::UInt +Value::asUInt() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); + JSON_ASSERT_MESSAGE( value_.int_ <= maxUInt, "signed integer out of UInt range" ); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE( value_.uint_ <= maxUInt, "unsigned integer out of UInt range" ); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); + return UInt( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE( "Type is not convertible to uint" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + + +# if defined(JSON_HAS_INT64) + +Value::Int64 +Value::asInt64() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + return value_.int_; + case uintValue: + JSON_ASSERT_MESSAGE( value_.uint_ <= UInt64(maxInt64), "unsigned integer out of Int64 range" ); + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= minInt64 && value_.real_ <= maxInt64, "Real out of Int64 range" ); + return Int( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE( "Type is not convertible to Int64" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + + +Value::UInt64 +Value::asUInt64() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to UInt64" ); + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt64, "Real out of UInt64 range" ); + return UInt( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE( "Type is not convertible to UInt64" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} +# endif // if defined(JSON_HAS_INT64) + + +LargestInt +Value::asLargestInt() const +{ +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + + +LargestUInt +Value::asLargestUInt() const +{ +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + + +double +Value::asDouble() const +{ + switch ( type_ ) + { + case nullValue: + return 0.0; + case intValue: + return static_cast( value_.int_ ); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast( value_.uint_ ); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE( "Type is not convertible to double" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +float +Value::asFloat() const +{ + switch ( type_ ) + { + case nullValue: + return 0.0f; + case intValue: + return static_cast( value_.int_ ); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast( value_.uint_ ); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + case stringValue: + case arrayValue: + case objectValue: + JSON_FAIL_MESSAGE( "Type is not convertible to float" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0.0f; // unreachable; +} + +bool +Value::asBool() const +{ + switch ( type_ ) + { + case nullValue: + return false; + case intValue: + case uintValue: + return value_.int_ != 0; + case realValue: + return value_.real_ != 0.0; + case booleanValue: + return value_.bool_; + case stringValue: + return value_.string_ && value_.string_[0] != 0; + case arrayValue: + case objectValue: + return value_.map_->size() != 0; + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + + +bool +Value::isConvertibleTo( ValueType other ) const +{ + switch ( type_ ) + { + case nullValue: + return true; + case intValue: + return ( other == nullValue && value_.int_ == 0 ) + || other == intValue + || ( other == uintValue && value_.int_ >= 0 ) + || other == realValue + || other == stringValue + || other == booleanValue; + case uintValue: + return ( other == nullValue && value_.uint_ == 0 ) + || ( other == intValue && value_.uint_ <= (unsigned)maxInt ) + || other == uintValue + || other == realValue + || other == stringValue + || other == booleanValue; + case realValue: + return ( other == nullValue && value_.real_ == 0.0 ) + || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt ) + || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt ) + || other == realValue + || other == stringValue + || other == booleanValue; + case booleanValue: + return ( other == nullValue && value_.bool_ == false ) + || other == intValue + || other == uintValue + || other == realValue + || other == stringValue + || other == booleanValue; + case stringValue: + return other == stringValue + || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) ); + case arrayValue: + return other == arrayValue + || ( other == nullValue && value_.map_->size() == 0 ); + case objectValue: + return other == objectValue + || ( other == nullValue && value_.map_->size() == 0 ); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + + +/// Number of values in array or object +ArrayIndex +Value::size() const +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: // size of the array is highest index + 1 + if ( !value_.map_->empty() ) + { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index()+1; + } + return 0; + case objectValue: + return ArrayIndex( value_.map_->size() ); +#else + case arrayValue: + return Int( value_.array_->size() ); + case objectValue: + return Int( value_.map_->size() ); +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + + +bool +Value::empty() const +{ + if ( isNull() || isArray() || isObject() ) + return size() == 0u; + else + return false; +} + + +bool +Value::operator!() const +{ + return isNull(); +} + + +void +Value::clear() +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); + + switch ( type_ ) + { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + default: + break; + } +} + +void +Value::resize( ArrayIndex newSize ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ArrayIndex oldSize = size(); + if ( newSize == 0 ) + clear(); + else if ( newSize > oldSize ) + (*this)[ newSize - 1 ]; + else + { + for ( ArrayIndex index = newSize; index < oldSize; ++index ) + { + value_.map_->erase( index ); + } + assert( size() == newSize ); + } +#else + value_.array_->resize( newSize ); +#endif +} + + +Value & +Value::operator[]( ArrayIndex index ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::iterator it = value_.map_->lower_bound( key ); + if ( it != value_.map_->end() && (*it).first == key ) + return (*it).second; + + ObjectValues::value_type defaultValue( key, null ); + it = value_.map_->insert( it, defaultValue ); + return (*it).second; +#else + return value_.array_->resolveReference( index ); +#endif +} + + +Value & +Value::operator[]( int index ) +{ + JSON_ASSERT( index >= 0 ); + return (*this)[ ArrayIndex(index) ]; +} + + +const Value & +Value::operator[]( ArrayIndex index ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::const_iterator it = value_.map_->find( key ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + Value *value = value_.array_->find( index ); + return value ? *value : null; +#endif +} + + +const Value & +Value::operator[]( int index ) const +{ + JSON_ASSERT( index >= 0 ); + return (*this)[ ArrayIndex(index) ]; +} + + +Value & +Value::operator[]( const char *key ) +{ + return resolveReference( key, false ); +} + + +Value & +Value::resolveReference( const char *key, + bool isStatic ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + *this = Value( objectValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, isStatic ? CZString::noDuplication + : CZString::duplicateOnCopy ); + ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); + if ( it != value_.map_->end() && (*it).first == actualKey ) + return (*it).second; + + ObjectValues::value_type defaultValue( actualKey, null ); + it = value_.map_->insert( it, defaultValue ); + Value &value = (*it).second; + return value; +#else + return value_.map_->resolveReference( key, isStatic ); +#endif +} + + +Value +Value::get( ArrayIndex index, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + + +bool +Value::isValidIndex( ArrayIndex index ) const +{ + return index < size(); +} + + + +const Value & +Value::operator[]( const char *key ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::const_iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + const Value *value = value_.map_->find( key ); + return value ? *value : null; +#endif +} + + +Value & +Value::operator[]( const std::string &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const std::string &key ) const +{ + return (*this)[ key.c_str() ]; +} + +Value & +Value::operator[]( const StaticString &key ) +{ + return resolveReference( key, true ); +} + + +# ifdef JSON_USE_CPPTL +Value & +Value::operator[]( const CppTL::ConstString &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const CppTL::ConstString &key ) const +{ + return (*this)[ key.c_str() ]; +} +# endif + + +Value & +Value::append( const Value &value ) +{ + return (*this)[size()] = value; +} + + +Value +Value::get( const char *key, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + + +Value +Value::get( const std::string &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} + +Value +Value::removeMember( const char* key ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value *value = value_.map_->find( key ); + if (value){ + Value old(*value); + value_.map_.remove( key ); + return old; + } else { + return null; + } +#endif +} + +Value +Value::removeMember( const std::string &key ) +{ + return removeMember( key.c_str() ); +} + +# ifdef JSON_USE_CPPTL +Value +Value::get( const CppTL::ConstString &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} +# endif + +bool +Value::isMember( const char *key ) const +{ + const Value *value = &((*this)[key]); + return value != &null; +} + + +bool +Value::isMember( const std::string &key ) const +{ + return isMember( key.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +bool +Value::isMember( const CppTL::ConstString &key ) const +{ + return isMember( key.c_str() ); +} +#endif + +Value::Members +Value::getMemberNames() const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return Value::Members(); + Members members; + members.reserve( value_.map_->size() ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for ( ; it != itEnd; ++it ) + members.push_back( std::string( (*it).first.c_str() ) ); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator( it ); + value_.map_->makeEndIterator( itEnd ); + for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) + members.push_back( std::string( ValueInternalMap::key( it ) ) ); +#endif + return members; +} +// +//# ifdef JSON_USE_CPPTL +//EnumMemberNames +//Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +//EnumValues +//Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + + +bool +Value::isNull() const +{ + return type_ == nullValue; +} + + +bool +Value::isBool() const +{ + return type_ == booleanValue; +} + + +bool +Value::isInt() const +{ + return type_ == intValue; +} + + +bool +Value::isUInt() const +{ + return type_ == uintValue; +} + + +bool +Value::isIntegral() const +{ + return type_ == intValue + || type_ == uintValue + || type_ == booleanValue; +} + + +bool +Value::isDouble() const +{ + return type_ == realValue; +} + + +bool +Value::isNumeric() const +{ + return isIntegral() || isDouble(); +} + + +bool +Value::isString() const +{ + return type_ == stringValue; +} + + +bool +Value::isArray() const +{ + return type_ == nullValue || type_ == arrayValue; +} + + +bool +Value::isObject() const +{ + return type_ == nullValue || type_ == objectValue; +} + + +void +Value::setComment( const char *comment, + CommentPlacement placement ) +{ + if ( !comments_ ) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment( comment ); +} + + +void +Value::setComment( const std::string &comment, + CommentPlacement placement ) +{ + setComment( comment.c_str(), placement ); +} + + +bool +Value::hasComment( CommentPlacement placement ) const +{ + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string +Value::getComment( CommentPlacement placement ) const +{ + if ( hasComment(placement) ) + return comments_[placement].comment_; + return ""; +} + + +std::string +Value::toStyledString() const +{ + StyledWriter writer; + return writer.write( *this ); +} + + +Value::const_iterator +Value::begin() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator +Value::end() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + + +Value::iterator +Value::begin() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator +Value::end() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return iterator(); +} + + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() + : kind_( kindNone ) +{ +} + + +PathArgument::PathArgument( ArrayIndex index ) + : index_( index ) + , kind_( kindIndex ) +{ +} + + +PathArgument::PathArgument( const char *key ) + : key_( key ) + , kind_( kindKey ) +{ +} + + +PathArgument::PathArgument( const std::string &key ) + : key_( key.c_str() ) + , kind_( kindKey ) +{ +} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path( const std::string &path, + const PathArgument &a1, + const PathArgument &a2, + const PathArgument &a3, + const PathArgument &a4, + const PathArgument &a5 ) +{ + InArgs in; + in.push_back( &a1 ); + in.push_back( &a2 ); + in.push_back( &a3 ); + in.push_back( &a4 ); + in.push_back( &a5 ); + makePath( path, in ); +} + + +void +Path::makePath( const std::string &path, + const InArgs &in ) +{ + const char *current = path.c_str(); + const char *end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while ( current != end ) + { + if ( *current == '[' ) + { + ++current; + if ( *current == '%' ) + addPathInArg( path, in, itInArg, PathArgument::kindIndex ); + else + { + ArrayIndex index = 0; + for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back( index ); + } + if ( current == end || *current++ != ']' ) + invalidPath( path, int(current - path.c_str()) ); + } + else if ( *current == '%' ) + { + addPathInArg( path, in, itInArg, PathArgument::kindKey ); + ++current; + } + else if ( *current == '.' ) + { + ++current; + } + else + { + const char *beginName = current; + while ( current != end && !strchr( "[.", *current ) ) + ++current; + args_.push_back( std::string( beginName, current ) ); + } + } +} + + +void +Path::addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ) +{ + if ( itInArg == in.end() ) + { + // Error: missing argument %d + } + else if ( (*itInArg)->kind_ != kind ) + { + // Error: bad argument type + } + else + { + args_.push_back( **itInArg ); + } +} + + +void +Path::invalidPath( const std::string &path, + int location ) +{ + // Error: invalid path. +} + + +const Value & +Path::resolve( const Value &root ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) + { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + { + // Error: unable to resolve path (object has no member named '' at position...) + } + } + } + return *node; +} + + +Value +Path::resolve( const Value &root, + const Value &defaultValue ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) + return defaultValue; + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + return defaultValue; + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + return defaultValue; + } + } + return *node; +} + + +Value & +Path::make( Value &root ) const +{ + Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() ) + { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + + +} // namespace Json diff --git a/dependencies/build/jsoncpp/json_valueiterator.inl b/dependencies/build/jsoncpp/json_valueiterator.inl new file mode 100644 index 00000000..7457ca38 --- /dev/null +++ b/dependencies/build/jsoncpp/json_valueiterator.inl @@ -0,0 +1,299 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_() + , isNull_( true ) +{ +} +#else + : isArray_( true ) + , isNull_( true ) +{ + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) + : current_( current ) + , isNull_( false ) +{ +} +#else +ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) + : isArray_( true ) +{ + iterator_.array_ = state; +} + + +ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) + : isArray_( false ) +{ + iterator_.map_ = state; +} +#endif + +Value & +ValueIteratorBase::deref() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if ( isArray_ ) + return ValueInternalArray::dereference( iterator_.array_ ); + return ValueInternalMap::value( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::increment() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if ( isArray_ ) + ValueInternalArray::increment( iterator_.array_ ); + ValueInternalMap::increment( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::decrement() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if ( isArray_ ) + ValueInternalArray::decrement( iterator_.array_ ); + ValueInternalMap::decrement( iterator_.map_ ); +#endif +} + + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP +# ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.current_; +# else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if ( isNull_ && other.isNull_ ) + { + return 0; + } + + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) + { + ++myDistance; + } + return myDistance; +# endif +#else + if ( isArray_ ) + return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +bool +ValueIteratorBase::isEqual( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if ( isNull_ ) + { + return other.isNull_; + } + return current_ == other.current_; +#else + if ( isArray_ ) + return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::copy( const SelfType &other ) +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; +#else + if ( isArray_ ) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + + +Value +ValueIteratorBase::key() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( czstring.c_str() ) + { + if ( czstring.isStaticString() ) + return Value( StaticString( czstring.c_str() ) ); + return Value( czstring.c_str() ); + } + return Value( czstring.index() ); +#else + if ( isArray_ ) + return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); + bool isStatic; + const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); + if ( isStatic ) + return Value( StaticString( memberName ) ); + return Value( memberName ); +#endif +} + + +UInt +ValueIteratorBase::index() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( !czstring.c_str() ) + return czstring.index(); + return Value::UInt( -1 ); +#else + if ( isArray_ ) + return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); + return Value::UInt( -1 ); +#endif +} + + +const char * +ValueIteratorBase::memberName() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char *name = (*current_).first.c_str(); + return name ? name : ""; +#else + if ( !isArray_ ) + return ValueInternalMap::key( iterator_.map_ ); + return ""; +#endif +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueConstIterator & +ValueConstIterator::operator =( const ValueIteratorBase &other ) +{ + copy( other ); + return *this; +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueIterator::ValueIterator( const ValueConstIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator::ValueIterator( const ValueIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator & +ValueIterator::operator =( const SelfType &other ) +{ + copy( other ); + return *this; +} + +} // namespace Json diff --git a/dependencies/build/jsoncpp/json_writer.cpp b/dependencies/build/jsoncpp/json_writer.cpp new file mode 100644 index 00000000..1bda183f --- /dev/null +++ b/dependencies/build/jsoncpp/json_writer.cpp @@ -0,0 +1,838 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include +# include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +static bool containsControlCharacter( const char* str ) +{ + while ( *str ) + { + if ( isControlCharacter( *(str++) ) ) + return true; + } + return false; +} + + +std::string valueToString( LargestInt value ) +{ + UIntToStringBuffer buffer; + char *current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if ( isNegative ) + value = -value; + uintToString( LargestUInt(value), current ); + if ( isNegative ) + *--current = '-'; + assert( current >= buffer ); + return current; +} + + +std::string valueToString( LargestUInt value ) +{ + UIntToStringBuffer buffer; + char *current = buffer + sizeof(buffer); + uintToString( value, current ); + assert( current >= buffer ); + return current; +} + +#if defined(JSON_HAS_INT64) + +std::string valueToString( Int value ) +{ + return valueToString( LargestInt(value) ); +} + + +std::string valueToString( UInt value ) +{ + return valueToString( LargestUInt(value) ); +} + +#endif // # if defined(JSON_HAS_INT64) + + +std::string valueToString( double value ) +{ + char buffer[32]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. + sprintf_s(buffer, sizeof(buffer), "%#.16g", value); +#else + sprintf(buffer, "%#.16g", value); +#endif + char* ch = buffer + strlen(buffer) - 1; + if (*ch != '0') return buffer; // nothing to truncate, so save time + while(ch > buffer && *ch == '0'){ + --ch; + } + char* last_nonzero = ch; + while(ch >= buffer){ + switch(*ch){ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + --ch; + continue; + case '.': + // Truncate zeroes to save bytes in output, but keep one. + *(last_nonzero+2) = '\0'; + return buffer; + default: + return buffer; + } + } + return buffer; +} + + +std::string valueToString( bool value ) +{ + return value ? "true" : "false"; +} + +std::string valueToQuotedString( const char *value ) +{ + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c=value; *c != 0; ++c) + { + switch(*c) + { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + //case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } + else + { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() +{ +} + + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_( false ) +{ +} + + +void +FastWriter::enableYAMLCompatibility() +{ + yamlCompatiblityEnabled_ = true; +} + + +std::string +FastWriter::write( const Value &root ) +{ + document_ = ""; + writeValue( root ); + document_ += "\n"; + return document_; +} + + +void +FastWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + document_ += "null"; + break; + case intValue: + document_ += valueToString( value.asLargestInt() ); + break; + case uintValue: + document_ += valueToString( value.asLargestUInt() ); + break; + case realValue: + document_ += valueToString( value.asDouble() ); + break; + case stringValue: + document_ += valueToQuotedString( value.asCString() ); + break; + case booleanValue: + document_ += valueToString( value.asBool() ); + break; + case arrayValue: + { + document_ += "["; + int size = value.size(); + for ( int index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ","; + writeValue( value[index] ); + } + document_ += "]"; + } + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + document_ += "{"; + for ( Value::Members::iterator it = members.begin(); + it != members.end(); + ++it ) + { + const std::string &name = *it; + if ( it != members.begin() ) + document_ += ","; + document_ += valueToQuotedString( name.c_str() ); + document_ += yamlCompatiblityEnabled_ ? ": " + : ":"; + writeValue( value[name] ); + } + document_ += "}"; + } + break; + } +} + + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_( 74 ) + , indentSize_( 3 ) +{ +} + + +std::string +StyledWriter::write( const Value &root ) +{ + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + document_ += "\n"; + return document_; +} + + +void +StyledWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asLargestInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asLargestUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + document_ += " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + for (;;) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + document_ += "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + + +bool +StyledWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + document_ += value; +} + + +void +StyledWriter::writeIndent() +{ + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + + +void +StyledWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + document_ += value; +} + + +void +StyledWriter::indent() +{ + indentString_ += std::string( indentSize_, ' ' ); +} + + +void +StyledWriter::unindent() +{ + assert( int(indentString_.size()) >= indentSize_ ); + indentString_.resize( indentString_.size() - indentSize_ ); +} + + +void +StyledWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + document_ += normalizeEOL( root.getComment( commentBefore ) ); + document_ += "\n"; +} + + +void +StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + document_ += "\n"; + document_ += normalizeEOL( root.getComment( commentAfter ) ); + document_ += "\n"; + } +} + + +bool +StyledWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter( std::string indentation ) + : document_(NULL) + , rightMargin_( 74 ) + , indentation_( indentation ) +{ +} + + +void +StyledStreamWriter::write( std::ostream &out, const Value &root ) +{ + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + + +void +StyledStreamWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asLargestInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asLargestUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + *document_ << " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledStreamWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + for (;;) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + *document_ << "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + + +bool +StyledStreamWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledStreamWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + *document_ << value; +} + + +void +StyledStreamWriter::writeIndent() +{ + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + + +void +StyledStreamWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + *document_ << value; +} + + +void +StyledStreamWriter::indent() +{ + indentString_ += indentation_; +} + + +void +StyledStreamWriter::unindent() +{ + assert( indentString_.size() >= indentation_.size() ); + indentString_.resize( indentString_.size() - indentation_.size() ); +} + + +void +StyledStreamWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + *document_ << normalizeEOL( root.getComment( commentBefore ) ); + *document_ << "\n"; +} + + +void +StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + *document_ << "\n"; + *document_ << normalizeEOL( root.getComment( commentAfter ) ); + *document_ << "\n"; + } +} + + +bool +StyledStreamWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledStreamWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +std::ostream& operator<<( std::ostream &sout, const Value &root ) +{ + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + + +} // namespace Json diff --git a/dependencies/build/jsoncpp/sconscript b/dependencies/build/jsoncpp/sconscript new file mode 100644 index 00000000..f6520d18 --- /dev/null +++ b/dependencies/build/jsoncpp/sconscript @@ -0,0 +1,8 @@ +Import( 'env buildLibrary' ) + +buildLibrary( env, Split( """ + json_reader.cpp + json_value.cpp + json_writer.cpp + """ ), + 'json' ) diff --git a/dependencies/include/json/autolink.h b/dependencies/include/json/autolink.h new file mode 100644 index 00000000..02328d1f --- /dev/null +++ b/dependencies/include/json/autolink.h @@ -0,0 +1,24 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_AUTOLINK_H_INCLUDED +# define JSON_AUTOLINK_H_INCLUDED + +# include "config.h" + +# ifdef JSON_IN_CPPTL +# include +# endif + +# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) +# define CPPTL_AUTOLINK_NAME "json" +# undef CPPTL_AUTOLINK_DLL +# ifdef JSON_DLL +# define CPPTL_AUTOLINK_DLL +# endif +# include "autolink.h" +# endif + +#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/dependencies/include/json/config.h b/dependencies/include/json/config.h new file mode 100644 index 00000000..7609d45e --- /dev/null +++ b/dependencies/include/json/config.h @@ -0,0 +1,96 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +# define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. +/// The memory pools allocator used optimization (initializing Value and ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +/// If defined, indicates that Json use exception to report invalid type manipulation +/// instead of C assert macro. +# define JSON_USE_EXCEPTION 1 + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + + +# ifdef JSON_IN_CPPTL +# include +# ifndef JSON_USE_CPPTL +# define JSON_USE_CPPTL 1 +# endif +# endif + +# ifdef JSON_IN_CPPTL +# define JSON_API CPPTL_API +# elif defined(JSON_DLL_BUILD) +# define JSON_API __declspec(dllexport) +# elif defined(JSON_DLL) +# define JSON_API __declspec(dllimport) +# else +# define JSON_API +# endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +# define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { + typedef int Int; + typedef unsigned int UInt; +# if defined(JSON_NO_INT64) + typedef int LargestInt; + typedef unsigned int LargestUInt; +# undef JSON_HAS_INT64 +# else // if defined(JSON_NO_INT64) + // For Microsoft Visual use specific types as long long is not supported +# if defined(_MSC_VER) // Microsoft Visual Studio + typedef __int64 Int64; + typedef unsigned __int64 UInt64; +# else // if defined(_MSC_VER) // Other platforms, use long long + typedef long long int Int64; + typedef unsigned long long int UInt64; +# endif // if defined(_MSC_VER) + typedef Int64 LargestInt; + typedef UInt64 LargestUInt; +# define JSON_HAS_INT64 +# endif // if defined(JSON_NO_INT64) +} // end namespace Json + + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/dependencies/include/json/features.h b/dependencies/include/json/features.h new file mode 100644 index 00000000..43532784 --- /dev/null +++ b/dependencies/include/json/features.h @@ -0,0 +1,49 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +# define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + + /** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ + class JSON_API Features + { + public: + /** \brief A configuration that allows all features and assumes all strings are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c false. + bool strictRoot_; + }; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/dependencies/include/json/forwards.h b/dependencies/include/json/forwards.h new file mode 100644 index 00000000..ab863da8 --- /dev/null +++ b/dependencies/include/json/forwards.h @@ -0,0 +1,44 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +# define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + + // writer.h + class FastWriter; + class StyledWriter; + + // reader.h + class Reader; + + // features.h + class Features; + + // value.h + typedef unsigned int ArrayIndex; + class StaticString; + class Path; + class PathArgument; + class Value; + class ValueIteratorBase; + class ValueIterator; + class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + class ValueMapAllocator; + class ValueInternalLink; + class ValueInternalArray; + class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +} // namespace Json + + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/dependencies/include/json/json.h b/dependencies/include/json/json.h new file mode 100644 index 00000000..da5fc967 --- /dev/null +++ b/dependencies/include/json/json.h @@ -0,0 +1,15 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_JSON_H_INCLUDED +# define JSON_JSON_H_INCLUDED + +# include "autolink.h" +# include "value.h" +# include "reader.h" +# include "writer.h" +# include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/dependencies/include/json/reader.h b/dependencies/include/json/reader.h new file mode 100644 index 00000000..0a324dfc --- /dev/null +++ b/dependencies/include/json/reader.h @@ -0,0 +1,214 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +# define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "features.h" +# include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include +# include +# include +# include + +namespace Json { + + /** \brief Unserialize a JSON document into a Value. + * + */ + class JSON_API Reader + { + public: + typedef char Char; + typedef const Char *Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader( const Features &features ); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const std::string &document, + Value &root, + bool collectComments = true ); + + /** \brief Read a Value from a JSON document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. + \ Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments = true ); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse( std::istream &is, + Value &root, + bool collectComments = true ); + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + + private: + enum TokenType + { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token + { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo + { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool expectToken( TokenType type, Token &token, const char *message ); + bool readToken( Token &token ); + void skipSpaces(); + bool match( Location pattern, + int patternLength ); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject( Token &token ); + bool readArray( Token &token ); + bool decodeNumber( Token &token ); + bool decodeString( Token &token ); + bool decodeString( Token &token, std::string &decoded ); + bool decodeDouble( Token &token ); + bool decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool addError( const std::string &message, + Token &token, + Location extra = 0 ); + bool recoverFromError( TokenType skipUntilToken ); + bool addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ); + void skipUntilSpace(); + Value ¤tValue(); + Char getNextChar(); + void getLocationLineAndColumn( Location location, + int &line, + int &column ) const; + std::string getLocationLineAndColumn( Location location ) const; + void addComment( Location begin, + Location end, + CommentPlacement placement ); + void skipCommentTokens( Token &token ); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value *lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; + }; + + /** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() + */ + std::istream& operator>>( std::istream&, Value& ); + +} // namespace Json + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/dependencies/include/json/value.h b/dependencies/include/json/value.h new file mode 100644 index 00000000..32e3455e --- /dev/null +++ b/dependencies/include/json/value.h @@ -0,0 +1,1103 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +# define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include +# include + +# ifndef JSON_USE_CPPTL_SMALLMAP +# include +# else +# include +# endif +# ifdef JSON_USE_CPPTL +# include +# endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + + /** \brief Type of the value held by a Value object. + */ + enum ValueType + { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). + }; + + enum CommentPlacement + { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for root value) + numberOfCommentPlacement + }; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + + /** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + class JSON_API StaticString + { + public: + explicit StaticString( const char *czstring ) + : str_( czstring ) + { + } + + operator const char *() const + { + return str_; + } + + const char *c_str() const + { + return str_; + } + + private: + const char *str_; + }; + + /** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. + * Non const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis default value in the case the required element + * does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + */ + class JSON_API Value + { + friend class ValueIteratorBase; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +# endif + public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +# if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value null; + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; + + private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +# ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString + { + public: + enum DuplicationPolicy + { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString( ArrayIndex index ); + CZString( const char *cstr, DuplicationPolicy allocate ); + CZString( const CZString &other ); + ~CZString(); + CZString &operator =( const CZString &other ); + bool operator<( const CZString &other ) const; + bool operator==( const CZString &other ) const; + ArrayIndex index() const; + const char *c_str() const; + bool isStaticString() const; + private: + void swap( CZString &other ); + const char *cstr_; + ArrayIndex index_; + }; + + public: +# ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +# else + typedef CppTL::SmallMap ObjectValues; +# endif // ifndef JSON_USE_CPPTL_SMALLMAP +# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. + This is useful since clear() and resize() will not alter types. + + Examples: + \code + Json::Value null_value; // null + Json::Value arr_value(Json::arrayValue); // [] + Json::Value obj_value(Json::objectValue); // {} + \endcode + */ + Value( ValueType type = nullValue ); + Value( Int value ); + Value( UInt value ); +#if defined(JSON_HAS_INT64) + Value( Int64 value ); + Value( UInt64 value ); +#endif // if defined(JSON_HAS_INT64) + Value( double value ); + Value( const char *value ); + Value( const char *beginValue, const char *endValue ); + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value( const StaticString &value ); + Value( const std::string &value ); +# ifdef JSON_USE_CPPTL + Value( const CppTL::ConstString &value ); +# endif + Value( bool value ); + Value( const Value &other ); + ~Value(); + + Value &operator=( const Value &other ); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap( Value &other ); + + ValueType type() const; + + bool operator <( const Value &other ) const; + bool operator <=( const Value &other ) const; + bool operator >=( const Value &other ) const; + bool operator >( const Value &other ) const; + + bool operator ==( const Value &other ) const; + bool operator !=( const Value &other ) const; + + int compare( const Value &other ) const; + + const char *asCString() const; + std::string asString() const; +# ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +# endif + Int asInt() const; + UInt asUInt() const; + Int64 asInt64() const; + UInt64 asUInt64() const; + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isUInt() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo( ValueType other ) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize( ArrayIndex size ); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[]( ArrayIndex index ); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[]( int index ); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[]( ArrayIndex index ) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[]( int index ) const; + + /// If the array contains at least index+1 elements, returns the element value, + /// otherwise returns defaultValue. + Value get( ArrayIndex index, + const Value &defaultValue ) const; + /// Return true if index < size(). + bool isValidIndex( ArrayIndex index ) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value &append( const Value &value ); + + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const char *key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const char *key ) const; + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const std::string &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const std::string &key ) const; + /** \brief Access an object value by name, create a null member if it does not exist. + + * If the object as no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value &operator[]( const StaticString &key ); +# ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const CppTL::ConstString &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const CppTL::ConstString &key ) const; +# endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const char *key, + const Value &defaultValue ) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const std::string &key, + const Value &defaultValue ) const; +# ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const CppTL::ConstString &key, + const Value &defaultValue ) const; +# endif + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + Value removeMember( const char* key ); + /// Same as removeMember(const char*) + Value removeMember( const std::string &key ); + + /// Return true if the object has a member named key. + bool isMember( const char *key ) const; + /// Return true if the object has a member named key. + bool isMember( const std::string &key ) const; +# ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember( const CppTL::ConstString &key ) const; +# endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + +//# ifdef JSON_USE_CPPTL +// EnumMemberNames enumMemberNames() const; +// EnumValues enumValues() const; +//# endif + + /// Comments must be //... or /* ... */ + void setComment( const char *comment, + CommentPlacement placement ); + /// Comments must be //... or /* ... */ + void setComment( const std::string &comment, + CommentPlacement placement ); + bool hasComment( CommentPlacement placement ) const; + /// Include delimiters and embedded newlines. + std::string getComment( CommentPlacement placement ) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + private: + Value &resolveReference( const char *key, + bool isStatic ); + +# ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const + { + return itemIsUsed_ == 0; + } + + inline void setItemUsed( bool isUsed = true ) + { + itemIsUsed_ = isUsed ? 1 : 0; + } + + inline bool isMemberNameStatic() const + { + return memberNameIsStatic_ == 0; + } + + inline void setMemberNameIsStatic( bool isStatic ) + { + memberNameIsStatic_ = isStatic ? 1 : 0; + } +# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP + + private: + struct CommentInfo + { + CommentInfo(); + ~CommentInfo(); + + void setComment( const char *text ); + + char *comment_; + }; + + //struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder + { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char *string_; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray *array_; + ValueInternalMap *map_; +#else + ObjectValues *map_; +# endif + } value_; + ValueType type_ : 8; + int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. +# ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. +# endif + CommentInfo *comments_; + }; + + + /** \brief Experimental and untested: represents an element of the "path" to access a node. + */ + class PathArgument + { + public: + friend class Path; + + PathArgument(); + PathArgument( ArrayIndex index ); + PathArgument( const char *key ); + PathArgument( const std::string &key ); + + private: + enum Kind + { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; + }; + + /** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ + class Path + { + public: + Path( const std::string &path, + const PathArgument &a1 = PathArgument(), + const PathArgument &a2 = PathArgument(), + const PathArgument &a3 = PathArgument(), + const PathArgument &a4 = PathArgument(), + const PathArgument &a5 = PathArgument() ); + + const Value &resolve( const Value &root ) const; + Value resolve( const Value &root, + const Value &defaultValue ) const; + /// Creates the "path" to access the specified node and returns a reference on the node. + Value &make( Value &root ) const; + + private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath( const std::string &path, + const InArgs &in ); + void addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ); + void invalidPath( const std::string &path, + int location ); + + Args args_; + }; + + + +#ifdef JSON_VALUE_USE_INTERNAL_MAP + /** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ + class JSON_API ValueMapAllocator + { + public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap *newMap() = 0; + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; + virtual void destructMap( ValueInternalMap *map ) = 0; + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; + virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; + virtual ValueInternalLink *allocateMapLink() = 0; + virtual void releaseMapLink( ValueInternalLink *link ) = 0; + }; + + /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ + class JSON_API ValueInternalLink + { + public: + enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char *keys_[itemPerLink]; + ValueInternalLink *previous_; + ValueInternalLink *next_; + }; + + + /** \brief A linked page based hash-table implementation used internally by Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following bucket. + * The last link of the last bucket is stored in tailLink_ as it has no following bucket. + * Only the last link of a bucket may contains 'available' item. The last link always + * contains at least one element unless is it the bucket one very first link. + */ + class JSON_API ValueInternalMap + { + friend class ValueIteratorBase; + friend class Value; + public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState + { + IteratorState() + : map_(0) + , link_(0) + , itemIndex_(0) + , bucketIndex_(0) + { + } + ValueInternalMap *map_; + ValueInternalLink *link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalMap(); + ValueInternalMap( const ValueInternalMap &other ); + ValueInternalMap &operator =( const ValueInternalMap &other ); + ~ValueInternalMap(); + + void swap( ValueInternalMap &other ); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta( BucketIndex growth ); + + bool reserve( BucketIndex newItemCount ); + + const Value *find( const char *key ) const; + + Value *find( const char *key ); + + Value &resolveReference( const char *key, + bool isStatic ); + + void remove( const char *key ); + + void doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ); + + ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); + + Value &setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ); + + Value &unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ); + + HashKey hash( const char *key ) const; + + int compare( const ValueInternalMap &other ) const; + + private: + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void incrementBucket( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static const char *key( const IteratorState &iterator ); + static const char *key( const IteratorState &iterator, bool &isStatic ); + static Value &value( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + + private: + ValueInternalLink *buckets_; + ValueInternalLink *tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; + }; + + /** \brief A simplified deque implementation used internally by Value. + * \internal + * It is based on a list of fixed "page", each page contains a fixed number of items. + * Instead of using a linked-list, a array of pointer is used for fast item look-up. + * Look-up for an element is as follow: + * - compute page index: pageIndex = itemIndex / itemsPerPage + * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] + * + * Insertion is amortized constant time (only the array containing the index of pointers + * need to be reallocated when items are appended). + */ + class JSON_API ValueInternalArray + { + friend class Value; + friend class ValueIteratorBase; + public: + enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() + : array_(0) + , currentPageIndex_(0) + , currentItemIndex_(0) + { + } + ValueInternalArray *array_; + Value **currentPageIndex_; + unsigned int currentItemIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalArray(); + ValueInternalArray( const ValueInternalArray &other ); + ValueInternalArray &operator =( const ValueInternalArray &other ); + ~ValueInternalArray(); + void swap( ValueInternalArray &other ); + + void clear(); + void resize( ArrayIndex newSize ); + + Value &resolveReference( ArrayIndex index ); + + Value *find( ArrayIndex index ) const; + + ArrayIndex size() const; + + int compare( const ValueInternalArray &other ) const; + + private: + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static Value &dereference( const IteratorState &iterator ); + static Value &unsafeDereference( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + static ArrayIndex indexOf( const IteratorState &iterator ); + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + void makeIterator( IteratorState &it, ArrayIndex index ) const; + + void makeIndexValid( ArrayIndex index ); + + Value **pages_; + ArrayIndex size_; + PageIndex pageCount_; + }; + + /** \brief Experimental: do not use. Allocator to customize Value internal array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destruct( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + \endcode + */ + class JSON_API ValueArrayAllocator + { + public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray *newArray() = 0; + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; + virtual void destructArray( ValueInternalArray *array ) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able to + * handle. + */ + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) = 0; + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) = 0; + virtual Value *allocateArrayPage() = 0; + virtual void releaseArrayPage( Value *value ) = 0; + }; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + + + /** \brief base class for Value iterators. + * + */ + class ValueIteratorBase + { + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); +#else + ValueIteratorBase( const ValueInternalArray::IteratorState &state ); + ValueIteratorBase( const ValueInternalMap::IteratorState &state ); +#endif + + bool operator ==( const SelfType &other ) const + { + return isEqual( other ); + } + + bool operator !=( const SelfType &other ) const + { + return !isEqual( other ); + } + + difference_type operator -( const SelfType &other ) const + { + return computeDistance( other ); + } + + /// Return either the index or the member name of the referenced value as a Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an objectValue. + const char *memberName() const; + + protected: + Value &deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance( const SelfType &other ) const; + + bool isEqual( const SelfType &other ) const; + + void copy( const SelfType &other ); + + private: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union + { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + } iterator_; + bool isArray_; +#endif + }; + + /** \brief const iterator for object and array value. + * + */ + class ValueConstIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef const Value &reference; + typedef const Value *pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueConstIterator( const ValueInternalArray::IteratorState &state ); + ValueConstIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + SelfType &operator =( const ValueIteratorBase &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + + /** \brief Iterator for object and array value. + */ + class ValueIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef Value &reference; + typedef Value *pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator( const ValueConstIterator &other ); + ValueIterator( const ValueIterator &other ); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueIterator( const ValueInternalArray::IteratorState &state ); + ValueIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + + SelfType &operator =( const SelfType &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + +} // namespace Json + + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/dependencies/include/json/writer.h b/dependencies/include/json/writer.h new file mode 100644 index 00000000..47893636 --- /dev/null +++ b/dependencies/include/json/writer.h @@ -0,0 +1,185 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +# define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include +# include +# include + +namespace Json { + + class Value; + + /** \brief Abstract class for writers. + */ + class JSON_API Writer + { + public: + virtual ~Writer(); + + virtual std::string write( const Value &root ) = 0; + }; + + /** \brief Outputs a Value in JSON format without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + */ + class JSON_API FastWriter : public Writer + { + public: + FastWriter(); + virtual ~FastWriter(){} + + void enableYAMLCompatibility(); + + public: // overridden from Writer + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + + std::string document_; + bool yamlCompatiblityEnabled_; + }; + + /** \brief Writes a Value in JSON format in a human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledWriter: public Writer + { + public: + StyledWriter(); + virtual ~StyledWriter(){} + + public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; + }; + + /** \brief Writes a Value in JSON format in a human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledStreamWriter + { + public: + StyledStreamWriter( std::string indentation="\t" ); + ~StyledStreamWriter(){} + + public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not return a value. + */ + void write( std::ostream &out, const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; + }; + +# if defined(JSON_HAS_INT64) + std::string JSON_API valueToString( Int value ); + std::string JSON_API valueToString( UInt value ); +# endif // if defined(JSON_HAS_INT64) + std::string JSON_API valueToString( LargestInt value ); + std::string JSON_API valueToString( LargestUInt value ); + std::string JSON_API valueToString( double value ); + std::string JSON_API valueToString( bool value ); + std::string JSON_API valueToQuotedString( const char *value ); + + /// \brief Output using the StyledStreamWriter. + /// \see Json::operator>>() + std::ostream& operator<<( std::ostream&, const Value &root ); + +} // namespace Json + + + +#endif // JSON_WRITER_H_INCLUDED diff --git a/include/boblight-functions.h b/include/boblight-functions.h new file mode 100644 index 00000000..0e0731e0 --- /dev/null +++ b/include/boblight-functions.h @@ -0,0 +1,41 @@ +/* + * boblight + * Copyright (C) Bob 2009 + * + * boblight is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * boblight is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +//these definitions can be expanded to make normal prototypes, or functionpointers and dlsym lines + +BOBLIGHT_FUNCTION(void*, boblight_init, ()); +BOBLIGHT_FUNCTION(void, boblight_destroy, (void* vpboblight)); + +BOBLIGHT_FUNCTION(int, boblight_connect, (void* vpboblight, const char* address, int port, int usectimeout)); +BOBLIGHT_FUNCTION(int, boblight_setpriority, (void* vpboblight, int priority)); +BOBLIGHT_FUNCTION(const char*, boblight_geterror, (void* vpboblight)); +BOBLIGHT_FUNCTION(int, boblight_getnrlights, (void* vpboblight)); +BOBLIGHT_FUNCTION(const char*, boblight_getlightname, (void* vpboblight, int lightnr)); + +BOBLIGHT_FUNCTION(int, boblight_getnroptions, (void* vpboblight)); +BOBLIGHT_FUNCTION(const char*, boblight_getoptiondescript,(void* vpboblight, int option)); +BOBLIGHT_FUNCTION(int, boblight_setoption, (void* vpboblight, int lightnr, const char* option)); +BOBLIGHT_FUNCTION(int, boblight_getoption, (void* vpboblight, int lightnr, const char* option, const char** output)); + +BOBLIGHT_FUNCTION(void, boblight_setscanrange, (void* vpboblight, int width, int height)); + +BOBLIGHT_FUNCTION(int, boblight_addpixel, (void* vpboblight, int lightnr, int* rgb)); +BOBLIGHT_FUNCTION(void, boblight_addpixelxy, (void* vpboblight, int x, int y, int* rgb)); + +BOBLIGHT_FUNCTION(int, boblight_sendrgb, (void* vpboblight, int sync, int* outputused)); +BOBLIGHT_FUNCTION(int, boblight_ping, (void* vpboblight, int* outputused)); diff --git a/include/boblight.h b/include/boblight.h new file mode 100644 index 00000000..c5963570 --- /dev/null +++ b/include/boblight.h @@ -0,0 +1,103 @@ +/* + * boblight + * Copyright (C) Bob 2009 + * + * boblight is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * boblight is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +//if you define BOBLIGHT_DLOPEN, all boblight functions are defined as pointers +//you can then call boblight_loadlibrary to load libboblight with dlopen and the function pointers with dlsym +//if you pass NULL to boblight_loadlibrary's first argument, the default filename for libboblight is used +//if boblight_loadlibrary returns NULL, dlopen and dlsym went ok, if not it returns a char* from dlerror + +//if you want to use the boblight functions from multiple files, you can define BOBLIGHT_DLOPEN in one file, +//and define BOBLIGHT_DLOPEN_EXTERN in the other file, the functionpointers are then defined as extern + +#ifndef LIBBOBLIGHT +#define LIBBOBLIGHT + + #if !defined(BOBLIGHT_DLOPEN) && !defined(BOBLIGHT_DLOPEN_EXTERN) + + #ifdef __cplusplus + extern "C" { + #endif + + //generate normal prototypes + #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) returnvalue name arguments + #include "boblight-functions.h" + #undef BOBLIGHT_FUNCTION + + #ifdef __cplusplus + } + #endif + + #elif defined(BOBLIGHT_DLOPEN) + + #include + #include + + //generate function pointers + #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) returnvalue (* name ) arguments = NULL + #include "boblight-functions.h" + #undef BOBLIGHT_FUNCTION + + #ifdef __cplusplus + #define BOBLIGHT_CAST(value) reinterpret_cast + #else + #define BOBLIGHT_CAST(value) (value) + #endif + + //gets a functionpointer from dlsym, and returns char* from dlerror if it didn't work + #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) \ + name = BOBLIGHT_CAST(returnvalue (*) arguments)(dlsym(p_boblight, #name)); \ + { char* error = dlerror(); if (error) return error; } + + void* p_boblight = NULL; //where we put the lib + + //load function pointers + char* boblight_loadlibrary(const char* filename) + { + if (filename == NULL) + filename = "libboblight.so"; + + if (p_boblight != NULL) + { + dlclose(p_boblight); + p_boblight = NULL; + } + + p_boblight = dlopen(filename, RTLD_NOW); + if (p_boblight == NULL) + return dlerror(); + + //generate dlsym lines + #include "boblight-functions.h" + + return NULL; + } + #undef BOBLIGHT_FUNCTION + #undef BOBLIGHT_CAST + + //you can define BOBLIGHT_DLOPEN_EXTERN when you load the library in another file + #elif defined(BOBLIGHT_DLOPEN_EXTERN) + + extern char* boblight_loadlibrary(const char* filename); + extern void* p_boblight; + #define BOBLIGHT_FUNCTION(returnvalue, name, arguments) extern returnvalue (* name ) arguments + #include "boblight-functions.h" + #undef BOBLIGHT_FUNCTION + + #endif //BOBLIGHT_DLOPEN_EXTERN +#endif //LIBBOBLIGHT + diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h new file mode 100644 index 00000000..1f3113de --- /dev/null +++ b/include/hyperion/Hyperion.h @@ -0,0 +1,41 @@ + +#pragma once + +// hyperion-utils includes +#include + +#include +#include +#include + +class Hyperion +{ +public: + Hyperion(const Json::Value& jsonConfig); + + ~Hyperion(); + + void setInputSize(const unsigned width, const unsigned height); + + RgbImage& image() + { + return *mImage; + } + + void commit(); + + void operator() (const RgbImage& inputImage); + + void setColor(const RgbColor& color); + +private: + void applyTransform(std::vector& colors) const; + + LedString mLedString; + + RgbImage* mImage; + + ImageToLedsMap mLedsMap; + + LedDevice* mDevice; +}; diff --git a/include/hyperion/ImageToLedsMap.h b/include/hyperion/ImageToLedsMap.h new file mode 100644 index 00000000..cc00972e --- /dev/null +++ b/include/hyperion/ImageToLedsMap.h @@ -0,0 +1,27 @@ + +#pragma once + +// hyperion-utils includes +#include + +// hyperion includes +#include + +class ImageToLedsMap +{ +public: + + ImageToLedsMap(); + + void createMapping(const RgbImage& image, const std::vector& leds); + + std::vector getMeanLedColor(); + + RgbColor findMeanColor(const std::vector& colors); + + std::vector getMedianLedColor(); + + RgbColor findMedianColor(std::vector& colors); +private: + std::vector > mColorsMap; +}; diff --git a/include/hyperion/LedDevice.h b/include/hyperion/LedDevice.h new file mode 100644 index 00000000..ea84fa7f --- /dev/null +++ b/include/hyperion/LedDevice.h @@ -0,0 +1,27 @@ +#pragma once + +// STL incldues +#include + +// Utility includes +#include + +class LedDevice +{ +public: + + /** + * Empty virtual destructor for pure virtual base class + */ + virtual ~LedDevice() + { + // empty + } + + /** + * Writes the RGB-Color values to the leds. + * + * @param[in] ledValues The RGB-color per led + */ + virtual int write(const std::vector& ledValues) = 0; +}; diff --git a/include/hyperion/LedString.h b/include/hyperion/LedString.h new file mode 100644 index 00000000..cc105b38 --- /dev/null +++ b/include/hyperion/LedString.h @@ -0,0 +1,72 @@ + +#pragma once + +// STL includes +#include +#include +#include + +// Local includes +#include + +// Forward class declarations +namespace Json { class Value; } + +/** + * The Led structure contains the definition of the image portion used to determine a single led's + * color. + *
+ * |--------------------image--|
+ * | minX  maxX                |
+ * |  |-----|maxY              |
+ * |  |     |                  |
+ * |  |-----|minY              |
+ * |                           |
+ * |                           |
+ * |                           |
+ * |---------------------------|
+ * 
+ */
+struct Led
+{
+	/** The index of the led */
+	unsigned index;
+
+	/** The minimum vertical scan line included for this leds color */
+	double minX_frac;
+	/** The maximum vertical scan line included for this leds color */
+	double maxX_frac;
+	/** The minimum horizontal scan line included for this leds color */
+	double minY_frac;
+	/** The maximum horizontal scan line included for this leds color */
+	double maxY_frac;
+};
+
+class LedString
+{
+public:
+	static LedString construct(const Json::Value& ledConfig, const Json::Value& colorConfig);
+
+	LedString();
+
+	~LedString();
+
+	const std::vector& leds() const;
+
+private:
+	std::vector mLeds;
+
+public:
+	/**
+	 * Color adjustements per color
+	 */
+	struct
+	{
+		/** The color gradient */
+		double gamma;
+		/** The color offset */
+		double adjust;
+		/** The minimum required level for the led to turn on */
+		double blacklevel;
+	} red, green, blue;
+};
diff --git a/include/utils/RgbColor.h b/include/utils/RgbColor.h
new file mode 100644
index 00000000..2b982aa0
--- /dev/null
+++ b/include/utils/RgbColor.h
@@ -0,0 +1,31 @@
+
+#pragma once
+
+// STL includes
+#include 
+#include 
+
+
+// Forward class declaration
+struct RgbColor;
+
+struct RgbColor
+{
+	uint8_t red;
+	uint8_t green;
+	uint8_t blue;
+
+	static RgbColor BLACK;
+	static RgbColor RED;
+	static RgbColor GREEN;
+	static RgbColor BLUE;
+	static RgbColor YELLOW;
+	static RgbColor WHITE;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const RgbColor& color)
+{
+	os << "{" << unsigned(color.red) << "," << unsigned(color.green) << "," << unsigned(color.blue) << "}";
+	return os;
+}
+
diff --git a/include/utils/RgbImage.h b/include/utils/RgbImage.h
new file mode 100644
index 00000000..0500a2a2
--- /dev/null
+++ b/include/utils/RgbImage.h
@@ -0,0 +1,56 @@
+
+#pragma once
+
+#include 
+#include 
+
+// Local includes
+#include "RgbColor.h"
+
+class RgbImage
+{
+public:
+
+	RgbImage(const unsigned width, const unsigned height, const RgbColor background = RgbColor::BLACK);
+
+	~RgbImage();
+
+	inline unsigned width() const
+	{
+		return mWidth;
+	}
+
+	inline unsigned height() const
+	{
+		return mHeight;
+	}
+
+	void setPixel(const unsigned x, const unsigned y, const RgbColor color);
+
+	const RgbColor& operator()(const unsigned x, const unsigned y) const;
+
+	RgbColor& operator()(const unsigned x, const unsigned y);
+
+	inline void copy(const RgbImage& other)
+	{
+		std::cout << "This image size: [" << width() << "x" << height() << "]. Other image size: [" << other.width() << "x" << other.height() << "]" << std::endl;
+		assert(other.mWidth == mWidth);
+		assert(other.mHeight == mHeight);
+
+		memcpy(mColors, other.mColors, mWidth*mHeight*sizeof(RgbColor));
+	}
+
+private:
+
+	inline unsigned toIndex(const unsigned x, const unsigned y) const
+	{
+		return y*mWidth + x;
+	}
+
+private:
+	unsigned mWidth;
+	unsigned mHeight;
+
+	/** The colors of the image */
+	RgbColor* mColors;
+};
diff --git a/include/utils/jsonschema/JsonFactory.h b/include/utils/jsonschema/JsonFactory.h
new file mode 100644
index 00000000..25774200
--- /dev/null
+++ b/include/utils/jsonschema/JsonFactory.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+// JSON-Schema includes
+#include 
+
+class JsonFactory
+{
+public:
+
+	static int load(const std::string& schema, const std::istream& config, Json::Value json);
+
+	static int load(const std::string& schema, const std::string& config, Json::Value& json)
+	{
+		// Load the schema and the config trees
+		Json::Value schemaTree = readJson(schema);
+		Json::Value configTree = readJson(config);
+
+		// create the validator
+		JsonSchemaChecker schemaChecker;
+		schemaChecker.setSchema(schemaTree);
+
+		bool valid = schemaChecker.validate(configTree);
+		for (const std::string& message : schemaChecker.getMessages())
+		{
+			std::cout << message << std::endl;
+		}
+		if (!valid)
+		{
+			std::cerr << "Validation failed for configuration file: " << config.c_str() << std::endl;
+			return -3;
+		}
+
+		json = configTree;
+		return 0;
+	}
+
+	static Json::Value readJson(const std::string& filename)
+	{
+		// Open the file input stream
+		std::ifstream ifs(filename.c_str());
+		return readJson(ifs);
+	}
+
+	static Json::Value readJson(std::istream& stream)
+	{
+		// will contains the root value after parsing.
+		Json::Value jsonTree;
+
+		Json::Reader reader;
+		if (! reader.parse(stream, jsonTree, false))
+		{
+			// report to the user the failure and their locations in the document.
+			std::stringstream sstream;
+			sstream << "Failed to parse configuration: " << reader.getFormattedErrorMessages().c_str();
+
+			throw std::runtime_error(sstream.str());
+		}
+		return jsonTree;
+	}
+};
diff --git a/include/utils/jsonschema/JsonSchemaChecker.h b/include/utils/jsonschema/JsonSchemaChecker.h
new file mode 100644
index 00000000..b76da8f3
--- /dev/null
+++ b/include/utils/jsonschema/JsonSchemaChecker.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 TNO, The Netherlands.
+//
+// This file contains information proprietary to TNO.
+//
+// Any disclosure or use of this information or any reproduction of this document or any part thereof for
+// other than the specified purpose for which it is intended is expressly prohibited except as TNO may
+// otherwise agree to in writing.
+
+#pragma once
+
+// stl includes
+#include 
+#include 
+
+// jsoncpp includes
+#include 
+
+/**
+ * JsonSchemaChecker is a very basic implementation of json schema.
+ * The json schema definition draft can be found at
+ * http://tools.ietf.org/html/draft-zyp-json-schema-03
+ *
+ * The following keywords are supported:
+ * - type
+ * - required
+ * - properties
+ * - items
+ * - enum
+ * - minimum
+ * - maximum
+ */
+class JsonSchemaChecker
+{
+public:
+	JsonSchemaChecker();
+	virtual ~JsonSchemaChecker();
+
+	bool setSchema(const Json::Value & schema);
+
+	bool validate(const Json::Value & value);
+
+	const std::list & getMessages() const;
+
+private:
+	void collectReferences(const Json::Value & schema);
+
+	void validate(const Json::Value &value, const Json::Value & schema);
+
+	void setMessage(const std::string & message);
+
+	void collectDependencies(const Json::Value & value, const Json::Value &schema);
+
+private:
+	// attribute check functions
+	void checkType(const Json::Value & value, const Json::Value & schema);
+	void checkProperties(const Json::Value & value, const Json::Value & schema);
+	void checkAdditionalProperties(const Json::Value & value, const Json::Value & schema, const Json::Value::Members & ignoredProperties);
+	void checkDependencies(const Json::Value & value, const Json::Value & schemaLink);
+	void checkMinimum(const Json::Value & value, const Json::Value & schema);
+	void checkMaximum(const Json::Value & value, const Json::Value & schema);
+	void checkItems(const Json::Value & value, const Json::Value & schema);
+	void checkMinItems(const Json::Value & value, const Json::Value & schema);
+	void checkMaxItems(const Json::Value & value, const Json::Value & schema);
+	void checkUniqueItems(const Json::Value & value, const Json::Value & schema);
+	void checkEnum(const Json::Value & value, const Json::Value & schema);
+
+private:
+	Json::Value _schema;
+
+	std::list _currentPath;
+	std::list _messages;
+	bool _error;
+
+	std::map _references; // ref 2 value
+};
diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt
new file mode 100644
index 00000000..2708724d
--- /dev/null
+++ b/libsrc/CMakeLists.txt
@@ -0,0 +1,15 @@
+
+# Define the current source locations
+SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include)
+SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc)
+
+add_library(bob2hyperion SHARED
+		bob2hyperion.cpp)
+
+target_link_libraries(bob2hyperion
+		hyperion
+		hyperion-utils)
+
+add_subdirectory(hyperion)
+add_subdirectory(hyperionpng)
+add_subdirectory(utils)
diff --git a/libsrc/bob2hyperion.cpp b/libsrc/bob2hyperion.cpp
new file mode 100644
index 00000000..a772b95a
--- /dev/null
+++ b/libsrc/bob2hyperion.cpp
@@ -0,0 +1,138 @@
+
+// SysLog includes
+#include 
+
+// Boblight includes
+#include "boblight.h"
+
+// JsonSchema includes
+#include 
+
+// Raspilight includes
+#include 
+
+static std::ofstream sDebugStream;
+
+inline Hyperion* rasp_cast(void* hyperion_ptr)
+{
+	return reinterpret_cast(hyperion_ptr);
+}
+
+void* boblight_init()
+{
+	syslog(LOG_INFO, __PRETTY_FUNCTION__);
+
+	const char* homeDir = getenv("RASPILIGHT_HOME");
+	if (!homeDir)
+	{
+		homeDir = "/etc";
+	}
+	syslog(LOG_INFO, "RASPILIGHT HOME DIR: %s", homeDir);
+
+	const std::string schemaFile = std::string(homeDir) + "/hyperion.schema.json";
+	const std::string configFile = std::string(homeDir) + "/hyperion.config.json";
+
+	Json::Value raspiConfig;
+	if (JsonFactory::load(schemaFile, configFile, raspiConfig) < 0)
+	{
+		syslog(LOG_WARNING, "UNABLE TO LOAD CONFIGURATION");
+		return 0;
+	}
+
+	Hyperion* raspiLight = new Hyperion(raspiConfig);
+	return reinterpret_cast(raspiLight);
+}
+
+void boblight_destroy(void* hyperion_ptr)
+{
+	syslog(LOG_INFO, __PRETTY_FUNCTION__);
+
+	Hyperion* raspiLight = rasp_cast(hyperion_ptr);
+
+	// Switch all leds to black (off)
+	raspiLight->setColor(RgbColor::BLACK);
+
+	delete raspiLight;
+
+	sDebugStream.close();
+}
+
+void boblight_setscanrange(void* hyperion_ptr, int width, int height)
+{
+	syslog(LOG_INFO, __PRETTY_FUNCTION__);
+	syslog(LOG_INFO, "Configuring scan range [%dx%d]", width, height);
+
+	Hyperion* raspiLight = rasp_cast(hyperion_ptr);
+	raspiLight->setInputSize(width, height);
+}
+
+void boblight_addpixelxy(void* hyperion_ptr, int x, int y, int* rgb)
+{
+	Hyperion* raspiLight = rasp_cast(hyperion_ptr);
+	const RgbColor color = {uint8_t(rgb[0]), uint8_t(rgb[1]), uint8_t(rgb[2])};
+	raspiLight->image().setPixel(x, y, color);
+}
+
+int boblight_sendrgb(void* hyperion_ptr, int sync, int* outputused)
+{
+	Hyperion* raspiLight = rasp_cast(hyperion_ptr);
+	raspiLight->commit();
+
+	return 1;
+}
+
+int boblight_connect(void* hyperion_ptr, const char* address, int port, int usectimeout)
+{
+	return 1;
+}
+
+const char* boblight_geterror(void* hyperion_ptr)
+{
+	return "ERROR";
+}
+
+int boblight_setpriority(void* hyperion_ptr, int priority)
+{
+	return 1;
+}
+
+
+int boblight_getnrlights(void* hyperion_ptr)
+{
+	return 1;
+}
+
+const char* boblight_getlightname(void* hyperion_ptr, int lightnr)
+{
+	return "LIGHT_NAME";
+}
+
+int boblight_getnroptions(void* hyperion_ptr)
+{
+	return 1;
+}
+
+const char* boblight_getoptiondescript(void* hyperion_ptr, int option)
+{
+	return "OPTION-DESCRIPTION";
+}
+
+int boblight_setoption(void* hyperion_ptr, int lightnr, const char* option)
+{
+	return 1;
+}
+
+int boblight_getoption(void* hyperion_ptr, int lightnr, const char* option, const char** output)
+{
+	return 1;
+}
+
+int boblight_addpixel(void* hyperion_ptr, int lightnr, int* rgb)
+{
+	return 1;
+}
+
+int boblight_ping(void* hyperion_ptr, int* outputused)
+{
+	return 1;
+}
diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt
new file mode 100644
index 00000000..29b8da41
--- /dev/null
+++ b/libsrc/hyperion/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+# Define the current source locations
+SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperion)
+SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperion)
+
+add_library(hyperion
+		${CURRENT_HEADER_DIR}/Hyperion.h
+		${CURRENT_HEADER_DIR}/LedDevice.h
+		${CURRENT_HEADER_DIR}/LedString.h
+		${CURRENT_HEADER_DIR}/ImageToLedsMap.h
+
+		${CURRENT_SOURCE_DIR}/LedDeviceWs2801.h
+		${CURRENT_SOURCE_DIR}/LedDeviceWs2801.cpp
+		${CURRENT_SOURCE_DIR}/LedString.cpp
+		${CURRENT_SOURCE_DIR}/Hyperion.cpp
+		${CURRENT_SOURCE_DIR}/ImageToLedsMap.cpp
+)
diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp
new file mode 100644
index 00000000..e8fd7e6a
--- /dev/null
+++ b/libsrc/hyperion/Hyperion.cpp
@@ -0,0 +1,101 @@
+
+// Syslog include
+#include 
+
+// JsonSchema include
+#include 
+
+// hyperion include
+#include 
+#include 
+
+#include "LedDeviceWs2801.h"
+
+LedDevice* constructDevice(const Json::Value& deviceConfig)
+{
+	std::cout << "Device configuration: " << deviceConfig << std::endl;
+	LedDevice* device = nullptr;
+	if (deviceConfig["type"].asString() == "ws2801")
+	{
+		const std::string name = "WS-2801";
+		const std::string output = deviceConfig["output"].asString();
+		const unsigned interval  = deviceConfig["interval"].asInt();
+		const unsigned rate      = deviceConfig["rate"].asInt();
+
+		LedDeviceWs2801* deviceWs2801 = new LedDeviceWs2801(name, output, interval, rate);
+		deviceWs2801->open();
+
+		device = deviceWs2801;
+	}
+	else
+	{
+		// Unknown / Unimplemented device
+	}
+	return device;
+}
+
+Hyperion::Hyperion(const Json::Value &jsonConfig) :
+	mLedString(LedString::construct(jsonConfig["leds"], jsonConfig["color"])),
+	mImage(nullptr),
+	mDevice(constructDevice(jsonConfig["device"]))
+{
+	// empty
+}
+
+
+Hyperion::~Hyperion()
+{
+	// Delete the existing image (or delete nullptr)
+	delete mImage;
+
+	// Delete the Led-String
+	delete mDevice;
+}
+
+void Hyperion::setInputSize(const unsigned width, const unsigned height)
+{
+	// Delete the existing image (or delete nullptr)
+	delete mImage;
+
+	// Create the new image with the mapping to the leds
+	mImage = new RgbImage(width, height);
+	mLedsMap.createMapping(*mImage, mLedString.leds());
+}
+
+void Hyperion::commit()
+{
+	// Derive the color per led
+	const std::vector ledColors = mLedsMap.getMedianLedColor();
+	// Write the Led colors to the led-string
+	mDevice->write(ledColors);
+}
+
+void Hyperion::operator() (const RgbImage& inputImage)
+{
+	std::cout << "Cached image size: [" << mImage->width() << "x" << mImage->height() << "]. Input image size: [" << inputImage.width() << "x" << inputImage.height() << "]" << std::endl;
+	// Copy the input-image into the buffer
+	mImage->copy(inputImage);
+
+	// Derive the color per led
+//	std::vector ledColors = mLedsMap.getMeanLedColor();
+	std::vector ledColors = mLedsMap.getMedianLedColor();
+	applyTransform(ledColors);
+
+	// Write the Led colors to the led-string
+	mDevice->write(ledColors);
+}
+
+void Hyperion::setColor(const RgbColor& color)
+{
+	mDevice->write(std::vector(mLedString.leds().size(), color));
+}
+
+void Hyperion::applyTransform(std::vector& colors) const
+{
+	for (RgbColor& color : colors)
+	{
+		color.red   = (color.red  < mLedString.red.blacklevel)?    0 : mLedString.red.adjust   + mLedString.red.gamma   * color.red;
+		color.green = (color.green < mLedString.green.blacklevel)? 0 : mLedString.green.adjust + mLedString.green.gamma * color.green;
+		color.blue  = (color.blue  < mLedString.blue.blacklevel)?  0 : mLedString.blue.adjust  + mLedString.blue.gamma  * color.blue;
+	}
+}
diff --git a/libsrc/hyperion/ImageToLedsMap.cpp b/libsrc/hyperion/ImageToLedsMap.cpp
new file mode 100644
index 00000000..2acd9a17
--- /dev/null
+++ b/libsrc/hyperion/ImageToLedsMap.cpp
@@ -0,0 +1,90 @@
+
+// STL includes
+#include 
+
+// hyperion includes
+#include 
+
+ImageToLedsMap::ImageToLedsMap()
+{
+	// empty
+}
+
+void ImageToLedsMap::createMapping(const RgbImage& image, const std::vector& leds)
+{
+	mColorsMap.resize(leds.size(), std::vector());
+
+	auto ledColors = mColorsMap.begin();
+	for (auto led = leds.begin(); ledColors != mColorsMap.end() && led != leds.end(); ++ledColors, ++led)
+	{
+		ledColors->clear();
+
+		const unsigned minX_idx = unsigned(image.width()  * led->minX_frac);
+		const unsigned maxX_idx = unsigned(image.width()  * led->maxX_frac);
+		const unsigned minY_idx = unsigned(image.height() * led->minY_frac);
+		const unsigned maxY_idx = unsigned(image.height() * led->maxY_frac);
+
+		for (unsigned y = minY_idx; y<=maxY_idx && ypush_back(&image(x,y));
+			}
+		}
+	}
+}
+
+std::vector ImageToLedsMap::getMeanLedColor()
+{
+	std::vector colors;
+	for (auto ledColors = mColorsMap.begin(); ledColors != mColorsMap.end(); ++ledColors)
+	{
+		const RgbColor color = findMeanColor(*ledColors);
+		colors.push_back(color);
+	}
+
+	return colors;
+}
+
+RgbColor ImageToLedsMap::findMeanColor(const std::vector& colors)
+{
+	uint_fast16_t cummRed   = 0;
+	uint_fast16_t cummGreen = 0;
+	uint_fast16_t cummBlue  = 0;
+	for (const RgbColor* color : colors)
+	{
+		cummRed   += color->red;
+		cummGreen += color->green;
+		cummBlue  += color->blue;
+	}
+
+	const uint8_t avgRed   = uint8_t(cummRed/colors.size());
+	const uint8_t avgGreen = uint8_t(cummGreen/colors.size());
+	const uint8_t avgBlue  = uint8_t(cummBlue/colors.size());
+
+	return {avgRed, avgGreen, avgBlue};
+}
+
+std::vector ImageToLedsMap::getMedianLedColor()
+{
+	std::vector ledColors;
+	for (std::vector& colors : mColorsMap)
+	{
+		const RgbColor color = findMedianColor(colors);
+		ledColors.push_back(color);
+	}
+
+	return ledColors;
+}
+
+RgbColor ImageToLedsMap::findMedianColor(std::vector& colors)
+{
+	std::sort(colors.begin(), colors.end(), [](const RgbColor* lhs, const RgbColor* rhs){ return lhs->red   < rhs->red;   });
+	const uint8_t red   = colors.at(colors.size()/2)->red;
+	std::sort(colors.begin(), colors.end(), [](const RgbColor* lhs, const RgbColor* rhs){ return lhs->green < rhs->green; });
+	const uint8_t green = colors.at(colors.size()/2)->green;
+	std::sort(colors.begin(), colors.end(), [](const RgbColor* lhs, const RgbColor* rhs){ return lhs->blue  < rhs->blue;  });
+	const uint8_t blue  = colors.at(colors.size()/2)->blue;
+
+	return {red, green, blue};
+}
diff --git a/libsrc/hyperion/LedDeviceWs2801.cpp b/libsrc/hyperion/LedDeviceWs2801.cpp
new file mode 100644
index 00000000..be4212cc
--- /dev/null
+++ b/libsrc/hyperion/LedDeviceWs2801.cpp
@@ -0,0 +1,85 @@
+
+// STL includes
+#include 
+#include 
+#include 
+
+// Linux includes
+#include 
+#include 
+
+// hyperion local includes
+#include "LedDeviceWs2801.h"
+
+LedDeviceWs2801::LedDeviceWs2801(const std::string& name,
+								 const std::string& outputDevice,
+								 const unsigned interval,
+								 const unsigned baudrate) :
+	mDeviceName(outputDevice),
+	mBaudRate_Hz(baudrate),
+	mFid(-1)
+{
+	memset(&spi, 0, sizeof(spi));
+
+	latchTime.tv_sec  = 0;
+	latchTime.tv_nsec = 500000;
+
+}
+
+LedDeviceWs2801::~LedDeviceWs2801()
+{
+//	close(mFid);
+}
+
+int LedDeviceWs2801::open()
+{
+	const int bitsPerWord = 8;
+
+	mFid = ::open(mDeviceName.c_str(), O_RDWR);
+
+	if (mFid < 0)
+	{
+		std::cerr << "Failed to open device('" << mDeviceName << "') " << std::endl;
+		return -1;
+	}
+
+	int mode = SPI_MODE_0;
+	if (ioctl(mFid, SPI_IOC_WR_MODE, &mode) == -1 || ioctl(mFid, SPI_IOC_RD_MODE, &mode) == -1)
+	{
+		return -2;
+	}
+
+	if (ioctl(mFid, SPI_IOC_WR_BITS_PER_WORD, &bitsPerWord) == -1 || ioctl(mFid, SPI_IOC_RD_BITS_PER_WORD, &bitsPerWord) == -1)
+	{
+		return -4;
+	}
+
+	if (ioctl(mFid, SPI_IOC_WR_MAX_SPEED_HZ, &mBaudRate_Hz) == -1 || ioctl(mFid, SPI_IOC_RD_MAX_SPEED_HZ, &mBaudRate_Hz) == -1)
+	{
+		return -6;
+	}
+
+	return 0;
+}
+
+int LedDeviceWs2801::write(const std::vector &ledValues)
+{
+	if (mFid < 0)
+	{
+		std::cerr << "Can not write to device which is open." << std::endl;
+		return -1;
+	}
+
+	spi.tx_buf = (__u64)ledValues.data();
+	spi.len    = ledValues.size() * sizeof(RgbColor);
+
+	int retVal = ioctl(mFid, SPI_IOC_MESSAGE(1), &spi);
+
+	if (retVal == 0)
+	{
+		// Sleep to latch the leds (only if write succesfull)
+		nanosleep(&latchTime, NULL);
+	}
+
+	return retVal;
+}
diff --git a/libsrc/hyperion/LedDeviceWs2801.h b/libsrc/hyperion/LedDeviceWs2801.h
new file mode 100644
index 00000000..f5fc05ad
--- /dev/null
+++ b/libsrc/hyperion/LedDeviceWs2801.h
@@ -0,0 +1,33 @@
+#pragma once
+
+// STL includes
+#include 
+
+// Linux-SPI includes
+#include 
+
+// hyperion incluse
+#include 
+
+class LedDeviceWs2801 : public LedDevice
+{
+public:
+	LedDeviceWs2801(const std::string& name,
+					const std::string& outputDevice,
+					const unsigned interval,
+					const unsigned baudrate);
+
+	virtual ~LedDeviceWs2801();
+
+	int open();
+
+	virtual int write(const std::vector &ledValues);
+
+private:
+	const std::string mDeviceName;
+	const int mBaudRate_Hz;
+
+	int mFid;
+	spi_ioc_transfer spi;
+	timespec latchTime;
+};
diff --git a/libsrc/hyperion/LedString.cpp b/libsrc/hyperion/LedString.cpp
new file mode 100644
index 00000000..fadb260e
--- /dev/null
+++ b/libsrc/hyperion/LedString.cpp
@@ -0,0 +1,60 @@
+// STL includes
+#include 
+#include 
+#include 
+
+// Json includes
+#include 
+
+// hyperion includes
+#include 
+
+LedString LedString::construct(const Json::Value& ledsConfig, const Json::Value& colorConfig)
+{
+	LedString ledString;
+
+	const Json::Value& redConfig   = colorConfig["red"];
+	const Json::Value& greenConfig = colorConfig["greem"];
+	const Json::Value& blueConfig  = colorConfig["blue"];
+
+	ledString.red.gamma      = redConfig["gamma"].asDouble();
+	ledString.red.adjust     = redConfig["adjust"].asDouble();
+	ledString.red.blacklevel = redConfig["blacklevel"].asDouble();
+
+	ledString.green.gamma      = greenConfig["gamma"].asDouble();
+	ledString.green.adjust     = colorConfig["adjust"].asDouble();
+	ledString.green.blacklevel = colorConfig["blacklevel"].asDouble();
+
+	ledString.blue.gamma      = blueConfig["gamma"].asDouble();
+	ledString.blue.adjust     = blueConfig["adjust"].asDouble();
+	ledString.blue.blacklevel = blueConfig["blacklevel"].asDouble();
+
+	for (const Json::Value& ledConfig : ledsConfig)
+	{
+		Led led;
+		led.index = ledConfig["index"].asInt();
+		const Json::Value& hscanConfig = ledConfig["hscan"];
+		const Json::Value& vscanConfig = ledConfig["vscan"];
+		led.minX_frac = std::max(0.0, std::min(100.0, hscanConfig["minimum"].asDouble()))/100.0;
+		led.maxX_frac = std::max(0.0, std::min(100.0, hscanConfig["maximum"].asDouble()))/100.0;
+		led.minY_frac = 1.0 - std::max(0.0, std::min(100.0, vscanConfig["maximum"].asDouble()))/100.0;
+		led.maxY_frac = 1.0 - std::max(0.0, std::min(100.0, vscanConfig["minimum"].asDouble()))/100.0;
+
+		ledString.mLeds.push_back(led);
+	}
+	return ledString;
+}
+
+LedString::LedString()
+{
+	// empty
+}
+
+LedString::~LedString()
+{
+}
+
+const std::vector& LedString::leds() const
+{
+	return mLeds;
+}
diff --git a/libsrc/hyperionpng/CMakeLists.txt b/libsrc/hyperionpng/CMakeLists.txt
new file mode 100644
index 00000000..0b7d19b9
--- /dev/null
+++ b/libsrc/hyperionpng/CMakeLists.txt
@@ -0,0 +1,19 @@
+
+# Find the libPNG
+find_package(PNG REQUIRED QUIET)
+
+# Add additional includes dirs
+include_directories(${PNG_INCLUDE_DIR})
+
+# Define the current source locations
+SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/hyperionpng)
+SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/hyperionpng)
+
+# Create the 'rasplight-png' library
+add_library(hyperion-png SHARED
+		${CURRENT_SOURCE_DIR}/hyperion-png.cpp
+		${CURRENT_SOURCE_DIR}/pngwriter.h
+		${CURRENT_SOURCE_DIR}/pngwriter.cc)
+
+target_link_libraries(hyperion-png
+		${PNG_LIBRARIES})
diff --git a/libsrc/hyperionpng/hyperion-png.cpp b/libsrc/hyperionpng/hyperion-png.cpp
new file mode 100644
index 00000000..f28d3cb3
--- /dev/null
+++ b/libsrc/hyperionpng/hyperion-png.cpp
@@ -0,0 +1,174 @@
+
+// STL includes
+#include 
+#include 
+#include 
+
+// Boblight includes
+#include 
+
+// PNGWriter includes
+#define NO_FREETYPE
+#include "pngwriter.h"
+
+struct RaspiPng
+{
+	pngwriter writer;
+	unsigned long fileIndex;
+
+	unsigned frameCnt;
+
+	std::ofstream logFile;
+};
+
+void* boblight_init()
+{
+	RaspiPng* raspiPng = new RaspiPng();
+
+	raspiPng->writer.pngwriter_rename("/home/pi/RASPI_0000.png");
+	raspiPng->fileIndex = 0;
+	raspiPng->frameCnt  = 0;
+	raspiPng->logFile.open("/home/pi/raspipng.log");
+
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return reinterpret_cast(raspiPng);
+}
+
+void boblight_destroy(void* vpboblight)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	raspiPng->logFile.close();
+	delete raspiPng;
+}
+
+void boblight_setscanrange(void* vpboblight, int width, int height)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << "(" << width << ", " << height << ")" << std::endl;
+
+	raspiPng->writer.resize(width, height);
+}
+
+void boblight_addpixelxy(void* vpboblight, int x, int y, int* rgb)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+
+	if (raspiPng->frameCnt%50 == 0)
+	{
+		// NB libpngwriter uses a one-based indexing scheme
+		raspiPng->writer.plot(x+1,y+1, rgb[0]/255.0, rgb[1]/255.0, rgb[2]/255.0);
+	}
+}
+
+int boblight_sendrgb(void* vpboblight, int sync, int* outputused)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << "(" << sync << ", outputused) FRAME " << raspiPng->frameCnt++ << std::endl;
+
+	if (raspiPng->frameCnt%50 == 0)
+	{
+		// Write-out the current frame and prepare for the next
+		raspiPng->writer.write_png();
+
+		++raspiPng->fileIndex;
+		char filename[64];
+
+		sprintf(filename, "/home/pi/RASPI_%04ld.png", raspiPng->fileIndex);
+
+		raspiPng->writer.pngwriter_rename(filename);
+	}
+
+	return 1;
+}
+
+int boblight_connect(void* vpboblight, const char* address, int port, int usectimeout)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return 1;
+}
+
+int boblight_setpriority(void* vpboblight, int priority)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return 1;
+}
+
+const char* boblight_geterror(void* vpboblight)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return "ERROR";
+}
+
+int boblight_getnrlights(void* vpboblight)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return 50;
+}
+
+const char* boblight_getlightname(void* vpboblight, int lightnr)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return "LIGHT";
+}
+
+int boblight_getnroptions(void* vpboblight)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return 1;
+}
+
+const char* boblight_getoptiondescript(void* vpboblight, int option)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return "";
+}
+
+int boblight_setoption(void* vpboblight, int lightnr, const char* option)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return 1;
+}
+
+int boblight_getoption(void* vpboblight, int lightnr, const char* option, const char** output)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return 1;
+}
+
+int boblight_addpixel(void* vpboblight, int lightnr, int* rgb)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return 1;
+}
+
+
+int boblight_ping(void* vpboblight, int* outputused)
+{
+	RaspiPng* raspiPng = reinterpret_cast(vpboblight);
+	raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
+
+	return 1;
+}
diff --git a/libsrc/hyperionpng/pngwriter.cc b/libsrc/hyperionpng/pngwriter.cc
new file mode 100644
index 00000000..ac1b88b4
--- /dev/null
+++ b/libsrc/hyperionpng/pngwriter.cc
@@ -0,0 +1,4722 @@
+//**********  pngwriter.cc   **********************************************
+//  Author:                    Paul Blackburn
+//
+//  Email:                     individual61@users.sourceforge.net
+//
+//  Version:                   0.5.4   (19 / II / 2009)
+//
+//  Description:               Library that allows plotting a 48 bit
+//                             PNG image pixel by pixel, which can
+//                             then be opened with a graphics program.
+//
+//  License:                   GNU General Public License
+//                             Copyright 2002, 2003, 2004, 2005, 2006, 2007, 
+//                             2008, 2009 Paul Blackburn
+//
+//  Website: Main:             http://pngwriter.sourceforge.net/
+//           Sourceforge.net:  http://sourceforge.net/projects/pngwriter/
+//           Freshmeat.net:    http://freshmeat.net/projects/pngwriter/
+//
+//  Documentation:             The header file (pngwriter.h) is commented, but for a
+//                             quick reference document, and support,
+//                             take a look at the website.
+//
+//*************************************************************************
+
+/*
+ *     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.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * */
+
+#define NO_FREETYPE
+#include "pngwriter.h"
+
+// Default Constructor
+////////////////////////////////////////////////////////////////////////////
+pngwriter::pngwriter()
+{
+
+   filename_ = new char[255];
+   textauthor_ = new char[255];
+   textdescription_ = new char[255];
+   texttitle_  = new char[255];
+   textsoftware_ = new char[255];
+
+   strcpy(filename_, "out.png");
+   width_ = 250;
+   height_ = 250;
+   backgroundcolour_ = 65535;
+   compressionlevel_ = -2;
+   filegamma_ = 0.5;
+   transformation_ = 0;
+
+   strcpy(textauthor_, "PNGwriter Author: Paul Blackburn");
+   strcpy(textdescription_, "http://pngwriter.sourceforge.net/");
+   strcpy(textsoftware_, "PNGwriter: An easy to use graphics library.");
+   strcpy(texttitle_, "out.png");
+
+   int kkkk;
+
+   bit_depth_ = 16; //Default bit depth for new images
+   colortype_=2;
+   screengamma_ = 2.2;
+
+   graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::pngwriter - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   for (kkkk = 0; kkkk < height_; kkkk++)
+     {
+        graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte));
+	if(graph_[kkkk] == NULL)
+	  {
+	     std::cerr << " PNGwriter::pngwriter - ERROR **:  Not able to allocate memory for image." << std::endl;
+	  }
+     }
+
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::pngwriter - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   int tempindex;
+   for(int hhh = 0; hhh65535)
+     {
+	std::cerr << " PNGwriter::pngwriter - WARNING **: Constructor called with background colour greater than 65535. Setting to 65535."<65535)
+     {
+	std::cerr << " PNGwriter::pngwriter - WARNING **: Constructor called with background colour greater than 1.0. Setting to 1.0."<65535)
+     {
+	std::cerr << " PNGwriter::pngwriter - WARNING **: Constructor called with background colour greater than 65535. Setting to 65535."<65535)
+     {
+	std::cerr << " PNGwriter::pngwriter - WARNING **: Constructor called with background colour greater than 65535. Setting to 65535."< 65535)
+     {
+	red = 65535;
+     }
+   if(green > 65535)
+     {
+	green = 65535;
+     }
+   if(blue > 65535)
+     {
+	blue = 65535;
+     }
+
+   if(red < 0)
+     {
+	red = 0;
+     }
+   if(green < 0)
+     {
+	green = 0;
+     }
+   if(blue < 0)
+     {
+	blue = 0;
+     }
+
+   if((bit_depth_ == 16))
+     {
+	//	if( (height_-y >-1) && (height_-y -1) && (6*(x-1)+5<6*width_) )
+	if( (y<=height_) && (y>0) && (x>0) && (x<=width_) )
+	  {
+	     //graph_[height_-y][6*(x-1) + i] where i goes from 0 to 5
+	     tempindex= 6*x-6;
+	     graph_[height_-y][tempindex] = (char) floor(((double)red)/256);
+	     graph_[height_-y][tempindex+1] = (char)(red%256);
+	     graph_[height_-y][tempindex+2] = (char) floor(((double)green)/256);
+	     graph_[height_-y][tempindex+3] = (char)(green%256);
+	     graph_[height_-y][tempindex+4] = (char) floor(((double)blue)/256);
+	     graph_[height_-y][tempindex+5] = (char)(blue%256);
+	  };
+
+	/*
+	 if(!( (height_-y >-1) && (height_-y -1) && (6*(x-1)+5<6*width_) ))
+	 {
+	 std::cerr << " PNGwriter::plot-- Plotting out of range! " << y << "   " << x << std::endl;
+	 }
+	 */
+     }
+
+   if((bit_depth_ == 8))
+     {
+	//	 if( (height_-y >-1) && (height_-y -1) && (3*(x-1)+5<3*width_) )
+	if( (y0) && (x>0) && (x-1) && (height_-y -1) && (6*(x-1)+5<6*width_) ))
+	 {
+	 std::cerr << " PNGwriter::plot-- Plotting out of range! " << y << "   " << x << std::endl;
+	 }
+	 */
+     }
+};
+
+void pngwriter::plot(int x, int y, double red, double green, double blue)
+{
+   this->plot(x,y,int(red*65535),int(green*65535),int(blue*65535));
+};
+
+///////////////////////////////////////////////////////////////
+int pngwriter::read(int x, int y, int colour)
+{
+   int temp1,temp2;
+
+   if((colour !=1)&&(colour !=2)&&(colour !=3))
+     {
+	std::cerr << " PNGwriter::read - WARNING **: Invalid argument: should be 1, 2 or 3, is " << colour << std::endl;
+	return 0;
+     }
+
+   if( ( x>0 ) && ( x <= (this->width_) ) && ( y>0 ) && ( y <= (this->height_) ) )
+     {
+
+	if(bit_depth_ == 16)
+	  {
+	     temp2=6*(x-1);
+	     if(colour == 1)
+	       {
+		  temp1 = (graph_[(height_-y)][temp2])*256 + graph_[height_-y][temp2+1];
+		  return temp1;
+	       }
+
+	     if(colour == 2)
+	       {
+		  temp1 = (graph_[height_-y][temp2+2])*256 + graph_[height_-y][temp2+3];
+		  return temp1;
+	       }
+
+	     if(colour == 3)
+	       {
+		  temp1 = (graph_[height_-y][temp2+4])*256 + graph_[height_-y][temp2+5];
+		  return temp1;
+	       }
+	  }
+
+	if(bit_depth_ == 8)
+	  {
+	     temp2=3*(x-1);
+	     if(colour == 1)
+	       {
+		  temp1 = graph_[height_-y][temp2];
+		  return temp1*256;
+	       }
+
+	     if(colour == 2)
+	       {
+		  temp1 =  graph_[height_-y][temp2+1];
+		  return temp1*256;
+	       }
+
+	     if(colour == 3)
+	       {
+		  temp1 =  graph_[height_-y][temp2+2];
+		  return temp1*256;
+	       }
+	  }
+     }
+   else
+     {
+	return 0;
+     }
+
+   std::cerr << " PNGwriter::read - WARNING **: Returning 0 because of bitdepth/colour type mismatch."<< std::endl;
+   return 0;
+}
+
+///////////////////////////////////////////////////////////////
+int pngwriter::read(int xxx, int yyy)
+{
+   int temp1,temp2,temp3,temp4,temp5;
+
+   if(
+      ( xxx>0 ) &&
+      ( xxx <= (this->width_) ) &&
+      ( yyy>0 ) &&
+      ( yyy <= (this->height_) )
+      )
+     {
+	if(bit_depth_ == 16)
+	  {
+	     //	temp1 = (graph_[(height_-yyy)][6*(xxx-1)])*256 + graph_[height_-yyy][6*(xxx-1)+1];
+	     temp5=6*xxx;
+	     temp1 = (graph_[(height_-yyy)][temp5-6])*256 + graph_[height_-yyy][temp5-5];
+	     temp2 = (graph_[height_-yyy][temp5-4])*256 + graph_[height_-yyy][temp5-3];
+	     temp3 = (graph_[height_-yyy][temp5-2])*256 + graph_[height_-yyy][temp5-1];
+	     temp4 =  int((temp1+temp2+temp3)/3.0);
+	  }
+	else if(bit_depth_ == 8)
+	  {
+	     //	temp1 = graph_[height_-yyy][3*(xxx-1)];
+	     temp5 = 3*xxx;
+	     temp1 = graph_[height_-yyy][temp5-3];
+	     temp2 =  graph_[height_-yyy][temp5-2];
+	     temp3 =  graph_[height_-yyy][temp5-1];
+	     temp4 =  int((temp1+temp2+temp3)/3.0);
+	  }
+	else
+	  {
+	     std::cerr << " PNGwriter::read - WARNING **: Invalid bit depth! Returning 0 as average value." << std::endl;
+	     temp4 = 0;
+	  }
+
+	return temp4;
+
+     }
+   else
+     {
+	return 0;
+     }
+}
+
+/////////////////////////////////////////////////////
+double  pngwriter::dread(int x, int y, int colour)
+{
+   return double(this->read(x,y,colour))/65535.0;
+}
+
+double  pngwriter::dread(int x, int y)
+{
+   return double(this->read(x,y))/65535.0;
+}
+
+///////////////////////////////////////////////////////
+void pngwriter::clear()
+{
+   int pen = 0;
+   int pencil = 0;
+   int tempindex;
+
+   if(bit_depth_==16)
+     {
+	for(pen = 0; pen 999999999)||(index < 0))
+     {
+	std::cerr << " PNGwriter::pngwriter_rename - ERROR **: Numerical name is out of 0 - 999 999 999 range (" << index <<")." << std::endl;
+	return;
+     }
+
+   if( 0>  sprintf(buffer, "%9.9lu.png",index))
+     {
+	std::cerr << " PNGwriter::pngwriter_rename - ERROR **: Error creating numerical filename." << std::endl;
+	return;
+     }
+
+   delete [] filename_;
+   delete [] texttitle_;
+
+   filename_ = new char[strlen(buffer)+1];
+   texttitle_ = new char[strlen(buffer)+1];
+
+   strcpy(filename_,buffer);
+   strcpy(texttitle_,buffer);
+
+};
+
+///////////////////////////////////////////////////////
+void pngwriter::settext(char * title, char * author, char * description, char * software)
+{
+   delete [] textauthor_;
+   delete [] textdescription_;
+   delete [] texttitle_;
+   delete [] textsoftware_;
+
+   textauthor_ = new char[strlen(author)+1];
+   textdescription_ = new char[strlen(description)+1];
+   textsoftware_ = new char[strlen(software)+1];
+   texttitle_ = new char[strlen(title)+1];
+
+   strcpy(texttitle_, title);
+   strcpy(textauthor_, author);
+   strcpy(textdescription_, description);
+   strcpy(textsoftware_, software);
+};
+
+///////////////////////////////////////////////////////
+void pngwriter::settext(const char * title, const char * author, const char * description, const char * software)
+{
+   delete [] textauthor_;
+   delete [] textdescription_;
+   delete [] texttitle_;
+   delete [] textsoftware_;
+
+   textauthor_ = new char[strlen(author)+1];
+   textdescription_ = new char[strlen(description)+1];
+   textsoftware_ = new char[strlen(software)+1];
+   texttitle_ = new char[strlen(title)+1];
+
+   strcpy(texttitle_, title);
+   strcpy(textauthor_, author);
+   strcpy(textdescription_, description);
+   strcpy(textsoftware_, software);
+};
+
+///////////////////////////////////////////////////////
+void pngwriter::close()
+{
+   FILE            *fp;
+   png_structp     png_ptr;
+   png_infop       info_ptr;
+
+   fp = fopen(filename_, "wb");
+   if( fp == NULL)
+     {
+	std::cerr << " PNGwriter::close - ERROR **: Error creating file (fopen() returned NULL pointer)." << std::endl;
+	perror(" PNGwriter::close - ERROR **");
+	return;
+     }
+
+   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+   info_ptr = png_create_info_struct(png_ptr);
+   png_init_io(png_ptr, fp);
+   if(compressionlevel_ != -2)
+     {
+	png_set_compression_level(png_ptr, compressionlevel_);
+     }
+   else
+     {
+	png_set_compression_level(png_ptr, PNGWRITER_DEFAULT_COMPRESSION);
+     }
+
+   png_set_IHDR(png_ptr, info_ptr, width_, height_,
+		bit_depth_, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+		PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+   if(filegamma_ < 1.0e-1)
+     {
+	filegamma_ = 0.5;  // Modified in 0.5.4 so as to be the same as the usual gamma.
+     }
+
+   png_set_gAMA(png_ptr, info_ptr, filegamma_);
+
+   time_t          gmt;
+   png_time        mod_time;
+   png_text        text_ptr[5];
+   time(&gmt);
+   png_convert_from_time_t(&mod_time, gmt);
+   png_set_tIME(png_ptr, info_ptr, &mod_time);
+   text_ptr[0].key = "Title";
+   text_ptr[0].text = texttitle_;
+   text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr[1].key = "Author";
+   text_ptr[1].text = textauthor_;
+   text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr[2].key = "Description";
+   text_ptr[2].text = textdescription_;
+   text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr[3].key = "Creation Time";
+   text_ptr[3].text = png_convert_to_rfc1123(png_ptr, &mod_time);
+   text_ptr[3].compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr[4].key = "Software";
+   text_ptr[4].text = textsoftware_;
+   text_ptr[4].compression = PNG_TEXT_COMPRESSION_NONE;
+   png_set_text(png_ptr, info_ptr, text_ptr, 5);
+
+   png_write_info(png_ptr, info_ptr);
+   png_write_image(png_ptr, graph_);
+   png_write_end(png_ptr, info_ptr);
+   png_destroy_write_struct(&png_ptr, &info_ptr);
+   fclose(fp);
+}
+
+//////////////////////////////////////////////////////
+void pngwriter::line(int xfrom, int yfrom, int xto, int yto, int red, int green,int  blue)
+{
+   //  Bresenham Algorithm.
+   //
+   int dy = yto - yfrom;
+   int dx = xto - xfrom;
+   int stepx, stepy;
+
+   if (dy < 0)
+     {
+	dy = -dy;  stepy = -1;
+     }
+   else
+     {
+	stepy = 1;
+     }
+
+   if (dx < 0)
+     {
+	dx = -dx;  stepx = -1;
+     }
+   else
+     {
+	stepx = 1;
+     }
+   dy <<= 1;     // dy is now 2*dy
+   dx <<= 1;     // dx is now 2*dx
+
+   this->plot(xfrom,yfrom,red,green,blue);
+
+   if (dx > dy)
+     {
+	int fraction = dy - (dx >> 1);
+
+	while (xfrom != xto)
+	  {
+	     if (fraction >= 0)
+	       {
+		  yfrom += stepy;
+		  fraction -= dx;
+	       }
+	     xfrom += stepx;
+	     fraction += dy;
+	     this->plot(xfrom,yfrom,red,green,blue);
+	  }
+     }
+   else
+     {
+	int fraction = dx - (dy >> 1);
+	while (yfrom != yto)
+	  {
+	     if (fraction >= 0)
+	       {
+		  xfrom += stepx;
+		  fraction -= dy;
+	       }
+	     yfrom += stepy;
+	     fraction += dx;
+	     this->plot(xfrom,yfrom,red,green,blue);
+	  }
+     }
+
+}
+
+void pngwriter::line(int xfrom, int yfrom, int xto, int yto, double red, double green,double  blue)
+{
+   this->line( xfrom,
+	       yfrom,
+	       xto,
+	       yto,
+	       int (red*65535),
+	       int (green*65535),
+	       int (blue*65535)
+	       );
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+void pngwriter::square(int xfrom, int yfrom, int xto, int yto, int red, int green, int blue)
+{
+   this->line(xfrom, yfrom, xfrom, yto, red, green, blue);
+   this->line(xto, yfrom, xto, yto, red, green, blue);
+   this->line(xfrom, yfrom, xto, yfrom, red, green, blue);
+   this->line(xfrom, yto, xto, yto, red, green, blue);
+}
+
+void pngwriter::square(int xfrom, int yfrom, int xto, int yto, double red, double green, double blue)
+{
+   this->square( xfrom,  yfrom,  xto,  yto, int(red*65535), int(green*65535), int(blue*65535));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+void pngwriter::filledsquare(int xfrom, int yfrom, int xto, int yto, int red, int green, int blue)
+{
+   for(int caca = xfrom; caca line(caca, yfrom, caca, yto, red, green, blue);
+     }
+}
+
+void pngwriter::filledsquare(int xfrom, int yfrom, int xto, int yto, double red, double green, double blue)
+{
+   this->filledsquare( xfrom,  yfrom,  xto,  yto, int(red*65535), int(green*65535), int(blue*65535));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+void pngwriter::circle(int xcentre, int ycentre, int radius, int red, int green, int blue)
+{
+   int x = 0;
+   int y = radius;
+   int p = (5 - radius*4)/4;
+
+   circle_aux(xcentre, ycentre, x, y, red, green, blue);
+   while (x < y)
+     {
+	x++;
+	if (p < 0)
+	  {
+	     p += 2*x+1;
+	  }
+	else
+	  {
+	     y--;
+	     p += 2*(x-y)+1;
+	  }
+	circle_aux(xcentre, ycentre, x, y, red, green, blue);
+     }
+}
+
+void pngwriter::circle(int xcentre, int ycentre, int radius, double red, double green, double blue)
+{
+   this->circle(xcentre,ycentre,radius, int(red*65535), int(green*65535), int(blue*65535));
+}
+
+////////////////////////////////////////////////////////////
+
+void pngwriter::circle_aux(int xcentre, int ycentre, int x, int y, int red, int green, int blue)
+{
+   if (x == 0)
+     {
+	this->plot( xcentre, ycentre + y, red, green, blue);
+	this->plot( xcentre, ycentre - y, red, green, blue);
+	this->plot( xcentre + y, ycentre, red, green, blue);
+	this->plot( xcentre - y, ycentre, red, green, blue);
+     }
+   else
+     if (x == y)
+       {
+	  this->plot( xcentre + x, ycentre + y, red, green, blue);
+	  this->plot( xcentre - x, ycentre + y, red, green, blue);
+	  this->plot( xcentre + x, ycentre - y, red, green, blue);
+	  this->plot( xcentre - x, ycentre - y, red, green, blue);
+       }
+   else
+     if (x < y)
+       {
+	  this->plot( xcentre + x, ycentre + y, red, green, blue);
+	  this->plot( xcentre - x, ycentre + y, red, green, blue);
+	  this->plot( xcentre + x, ycentre - y, red, green, blue);
+	  this->plot( xcentre - x, ycentre - y, red, green, blue);
+	  this->plot( xcentre + y, ycentre + x, red, green, blue);
+	  this->plot( xcentre - y, ycentre + x, red, green, blue);
+	  this->plot( xcentre + y, ycentre - x, red, green, blue);
+	  this->plot( xcentre - y, ycentre - x, red, green, blue);
+       }
+
+}
+
+////////////////////////////////////////////////////////////
+void pngwriter::filledcircle(int xcentre, int ycentre, int radius, int red, int green, int blue)
+{
+   for(int jjj = ycentre-radius; jjj< ycentre+radius+1; jjj++)
+     {
+	this->line(xcentre - int(sqrt((double)(radius*radius) - (-ycentre + jjj)*(-ycentre + jjj ))), jjj,
+		   xcentre + int(sqrt((double)(radius*radius) - (-ycentre + jjj)*(-ycentre + jjj ))),jjj,red,green,blue);
+     }
+}
+
+void pngwriter::filledcircle(int xcentre, int ycentre, int radius, double red, double green, double blue)
+{
+   this->filledcircle( xcentre, ycentre,  radius, int(red*65535), int(green*65535), int(blue*65535));
+}
+
+////////////////Reading routines/////////////////////
+/////////////////////////////////////////////////
+
+// Modified with Mikkel's patch
+void pngwriter::readfromfile(char * name)
+{
+   FILE            *fp;
+   png_structp     png_ptr;
+   png_infop       info_ptr;
+   unsigned char   **image;
+   unsigned long   width, height;
+   int bit_depth, color_type, interlace_type;
+   //   png_uint_32     i;
+   //
+   fp = fopen (name,"rb");
+   if (fp==NULL)
+     {
+	std::cerr << " PNGwriter::readfromfile - ERROR **: Error opening file \"" << std::flush;
+	std::cerr << name <readfromfile((char *)(name));
+}
+
+/////////////////////////////////////////////////////////
+int pngwriter::check_if_png(char *file_name, FILE **fp)
+{
+   char    sig[PNG_BYTES_TO_CHECK];
+
+   if ( /*(*fp = fopen(file_name, "rb")) */  *fp == NULL) // Fixed 10 10 04
+     {
+	//   exit(EXIT_FAILURE);
+	std::cerr << " PNGwriter::check_if_png - ERROR **: Could not open file  " << file_name << " to read." << std::endl;
+	perror(" PNGwriter::check_if_png - ERROR **");
+	return 0;
+     }
+
+   if (fread(sig, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK)
+     {
+	//exit(EXIT_FAILURE);
+	std::cerr << " PNGwriter::check_if_png - ERROR **: File " << file_name << " does not appear to be a valid PNG file." << std::endl;
+	perror(" PNGwriter::check_if_png - ERROR **");
+	fclose(*fp);
+	return 0;
+     }
+   
+   if (png_sig_cmp( (png_bytep) sig, (png_size_t)0, PNG_BYTES_TO_CHECK) /*png_check_sig((png_bytep) sig, PNG_BYTES_TO_CHECK)*/ ) 
+     {
+	std::cerr << " PNGwriter::check_if_png - ERROR **: File " << file_name << " does not appear to be a valid PNG file. png_check_sig() failed." << std::endl;
+	fclose(*fp);
+	return 0;
+     }
+   
+   
+   
+   return 1; //Success
+}
+
+///////////////////////////////////////////////////////
+int pngwriter::read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr)
+{
+   *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+   if (*png_ptr == NULL)
+     {
+	std::cerr << " PNGwriter::read_png_info - ERROR **: Could not create read_struct." << std::endl;
+	fclose(fp);
+	return 0;
+	//exit(EXIT_FAILURE);
+     }
+   *info_ptr = png_create_info_struct(*png_ptr);
+   if (*info_ptr == NULL)
+     {
+	png_destroy_read_struct(png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+	std::cerr << " PNGwriter::read_png_info - ERROR **: Could not create info_struct." << std::endl;
+	//exit(EXIT_FAILURE);
+	fclose(fp);
+	return 0;
+     }
+   if (setjmp((*png_ptr)->jmpbuf)) /*(setjmp(png_jmpbuf(*png_ptr)) )*//////////////////////////////////////
+     {
+	png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL);
+	std::cerr << " PNGwriter::read_png_info - ERROR **: This file may be a corrupted PNG file. (setjmp(*png_ptr)->jmpbf) failed)." << std::endl;
+	fclose(fp);
+	return 0;
+	//exit(EXIT_FAILURE);
+     }
+   png_init_io(*png_ptr, fp);
+   png_set_sig_bytes(*png_ptr, PNG_BYTES_TO_CHECK);
+   png_read_info(*png_ptr, *info_ptr);
+
+   return 1;
+}
+
+////////////////////////////////////////////////////////////
+int pngwriter::read_png_image(FILE *fp, png_structp png_ptr, png_infop info_ptr,
+			      png_bytepp *image, png_uint_32 *width, png_uint_32 *height)
+{
+   unsigned int i,j;
+
+   *width = png_get_image_width(png_ptr, info_ptr);
+   *height = png_get_image_height(png_ptr, info_ptr);
+
+   if( width == NULL)
+     {
+	std::cerr << " PNGwriter::read_png_image - ERROR **: png_get_image_width() returned NULL pointer." << std::endl;
+	fclose(fp);
+	return 0;
+     }
+
+   if( height == NULL)
+     {
+	std::cerr << " PNGwriter::read_png_image - ERROR **: png_get_image_height() returned NULL pointer." << std::endl;
+	fclose(fp);
+	return 0;
+     }
+
+   if ((*image = (png_bytepp)malloc(*height * sizeof(png_bytep))) == NULL)
+     {
+	std::cerr << " PNGwriter::read_png_image - ERROR **: Could not allocate memory for reading image." << std::endl;
+	fclose(fp);
+	return 0;
+	//exit(EXIT_FAILURE);
+     }
+   for (i = 0; i < *height; i++)
+     {
+	(*image)[i] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
+	if ((*image)[i] == NULL)
+	  {
+	     for (j = 0; j < i; j++) free((*image)[j]);
+	     free(*image);
+	     fclose(fp);
+	     std::cerr << " PNGwriter::read_png_image - ERROR **: Could not allocate memory for reading image." << std::endl;
+	     return 0;
+	     //exit(EXIT_FAILURE);
+	  }
+     }
+   png_read_image(png_ptr, *image);
+
+   return 1;
+}
+
+///////////////////////////////////
+int pngwriter::getheight(void)
+{
+   return height_;
+}
+
+int pngwriter::getwidth(void)
+{
+   return width_;
+}
+
+
+int pngwriter::getbitdepth(void)
+{
+   return bit_depth_;
+}
+
+int pngwriter::getcolortype(void)
+{
+   return colortype_;
+}
+
+double pngwriter::getgamma(void)
+{
+   return filegamma_;
+}
+
+void pngwriter::setgamma(double gamma)
+{
+   filegamma_ = gamma;
+}
+
+// The algorithms HSVtoRGB and RGBtoHSV were found at http://www.cs.rit.edu/~ncs/
+//  which is a page that belongs to Nan C. Schaller, though
+//  these algorithms appear to be the work of Eugene Vishnevsky.
+//////////////////////////////////////////////
+void pngwriter::HSVtoRGB( double *r, double *g, double *b, double h, double s, double v )
+{
+   // r,g,b values are from 0 to 1
+   // h = [0,1], s = [0,1], v = [0,1]
+   // if s == 0, then h = -1 (undefined)
+   //
+   h = h*360.0;
+
+   int i;
+   double f, p, q, t;
+   if( s == 0 )
+     {
+	// achromatic (grey)
+	*r = *g = *b = v;
+	return;
+     }
+
+   h /= 60;                        // sector 0 to 5
+   i = int(floor( h ));
+   f = h - i;                      // factorial part of h
+   p = v * ( 1 - s );
+   q = v * ( 1 - s * f );
+   t = v * ( 1 - s * ( 1 - f ) );
+
+   switch( i )
+     {
+      case 0:
+	*r = v;
+	*g = t;
+	*b = p;
+	break;
+      case 1:
+	*r = q;
+	*g = v;
+	*b = p;
+	break;
+      case 2:
+	*r = p;
+	*g = v;
+	*b = t;
+	break;
+      case 3:
+	*r = p;
+	*g = q;
+	*b = v;
+	break;
+      case 4:
+	*r = t;
+	*g = p;
+	*b = v;
+	break;
+      default:                // case 5:
+	*r = v;
+	*g = p;
+	*b = q;
+	break;
+     }
+}
+
+void pngwriter::RGBtoHSV( float r, float g, float b, float *h, float *s, float *v )
+{
+
+   float min=0.0; //These values are not used.
+   float max=1.0;
+   float delta;
+
+   if( (r>=g)&&(r>=b) )
+     {
+	max = r;
+     }
+   if( (g>=r)&&(g>=b) )
+     {
+	max = g;
+     }
+   if( (b>=g)&&(b>=r) )
+     {
+	max = b;
+     }
+
+   if( (r<=g)&&(r<=b) )
+     {
+	min = r;
+     }
+   if( (g<=r)&&(g<=b) )
+     {
+	min = g;
+     }
+   if( (b<=g)&&(b<=r) )
+     {
+	min = b;
+     }
+
+   *v = max;                               // v
+
+   delta = max - min;
+
+   if( max != 0 )
+     *s = delta / max;               // s
+   else
+     {
+
+	r = g = b = 0;                // s = 0, v is undefined
+	*s = 0;
+	*h = -1;
+	return;
+     }
+
+   if( r == max )
+     *h = ( g - b ) / delta;         // between yellow & magenta
+   else if( g == max )
+     *h = 2 + ( b - r ) / delta;     // between cyan & yellow
+   else
+     *h = 4 + ( r - g ) / delta;     // between magenta & cyan
+
+   *h *= 60;                               // degrees
+   if( *h < 0 )
+     *h += 360;
+
+}
+
+//
+//////////////////////////////////////////////////////////////////////////////////
+void pngwriter::plotHSV(int x, int y, double hue, double saturation, double value)
+{
+   double red,green,blue;
+   double *redp;
+   double *greenp;
+   double *bluep;
+
+   redp = &red;
+   greenp = &green;
+   bluep = &blue;
+
+   HSVtoRGB(redp,greenp,bluep,hue,saturation,value);
+   plot(x,y,red,green,blue);
+}
+
+void pngwriter::plotHSV(int x, int y, int hue, int saturation, int value)
+{
+   plotHSV(x, y, double(hue)/65535.0, double(saturation)/65535.0,  double(value)/65535.0);
+}
+
+//
+//////////////////////////////////////////////////////////////////////////////////
+double pngwriter::dreadHSV(int x, int y, int colour)
+{
+   if( (x>0)&&(x<=width_)&&(y>0)&&(y<=height_) )
+     {
+
+	float * huep;
+	float * saturationp;
+	float * valuep;
+	float red,green,blue;
+	float hue, saturation, value;
+
+	red = float(dread(x,y,1));
+	green = float(dread(x,y,2));
+	blue = float(dread(x,y,3));
+
+	huep = &hue;
+	saturationp = &saturation;
+	valuep = &value;
+
+	RGBtoHSV( red,  green,  blue, huep,  saturationp, valuep );
+
+	if(colour == 1)
+	  {
+	     return double(hue)/360.0;
+	  }
+
+	else if(colour == 2)
+	  {
+	     return saturation;
+	  }
+
+	else if(colour == 3)
+	  {
+	     return value;
+	  }
+
+	std::cerr << " PNGwriter::dreadHSV - ERROR **: Called with wrong colour argument: should be 1, 2 or 3; was: " << colour << "." << std::endl;
+     }
+   return 0.0;
+}
+
+//
+//////////////////////////////////////////////////////////////////////////////////
+int pngwriter::readHSV(int x, int y, int colour)
+{
+   if( (x>0)&&(x<=width_)&&(y>0)&&(y<=height_) )
+     {
+
+	float * huep;
+	float * saturationp;
+	float * valuep;
+	float red,green,blue;
+	float hue, saturation, value;
+
+	red = float(dread(x,y,1));
+	green = float(dread(x,y,2));
+	blue = float(dread(x,y,3));
+
+	huep = &hue;
+	saturationp = &saturation;
+	valuep = &value;
+
+	RGBtoHSV( red,  green,  blue, huep,  saturationp, valuep );
+
+	if(colour == 1)
+	  {
+	     return int(65535*(double(hue)/360.0));
+	  }
+
+	else if(colour == 2)
+	  {
+	     return int(65535*saturation);
+	  }
+
+	else if(colour == 3)
+	  {
+	     return int(65535*value);
+	  }
+
+	std::cerr << " PNGwriter::readHSV - ERROR **: Called with wrong colour argument: should be 1, 2 or 3; was: " << colour << "." << std::endl;
+	return 0;
+     }
+   else
+     {
+	return 0;
+     }
+}
+
+void pngwriter::setcompressionlevel(int level)
+{
+   if( (level < -1)||(level > 9) )
+     {
+	std::cerr << " PNGwriter::setcompressionlevel - ERROR **: Called with wrong compression level: should be -1 to 9, was: " << level << "." << std::endl;
+     }
+   compressionlevel_ = level;
+}
+
+// An implementation of a Bezier curve.
+void pngwriter::bezier(  int startPtX, int startPtY,
+			 int startControlX, int startControlY,
+			 int endPtX, int endPtY,
+			 int endControlX, int endControlY,
+			 double red, double green, double blue)
+{
+
+   double cx = 3.0*(startControlX - startPtX);
+   double bx = 3.0*(endControlX - startControlX) - cx;
+   double ax = double(endPtX - startPtX - cx - bx);
+
+   double cy = 3.0*(startControlY - startPtY);
+   double by = 3.0*(endControlY - startControlY) - cy;
+   double ay = double(endPtY - startPtY - cy - by);
+
+   double x,y,newx,newy;
+   x = startPtX;
+   y = startPtY;
+
+   for(double t = 0.0; t<=1.005; t += 0.005)
+     {
+	newx = startPtX + t*(double(cx) + t*(double(bx) + t*(double(ax))));
+	newy = startPtY + t*(double(cy) + t*(double(by) + t*(double(ay))));
+	this->line(int(x),int(y),int(newx),int(newy),red,green,blue);
+	x = newx;
+	y = newy;
+     }
+}
+
+//int version of bezier
+void pngwriter::bezier(  int startPtX, int startPtY,
+			 int startControlX, int startControlY,
+			 int endPtX, int endPtY,
+			 int endControlX, int endControlY,
+			 int  red, int  green, int blue)
+{
+   this->bezier(   startPtX,  startPtY,
+		   startControlX, startControlY,
+		   endPtX, endPtY,
+		   endControlX,  endControlY,
+		   double(red)/65535.0,  double(green)/65535.0,  double(blue)/65535.0);
+}
+
+/*
+int pngwriter::getcompressionlevel(void)
+{
+   return png_get_compression_level(png_ptr);
+}
+*/
+
+double pngwriter::version(void)
+{
+   const char *a = "Jeramy Webb (jeramyw@gmail.com), Mike Heller (mkheller@gmail.com)"; // For their generosity ;-)
+   char b = a[27];
+   b++;
+   return (PNGWRITER_VERSION);
+}
+
+void pngwriter::write_png(void)
+{
+   this->close();
+}
+
+#ifndef NO_FREETYPE
+
+// Freetype-based text rendering functions.
+///////////////////////////////////////////
+void pngwriter::plot_text( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue)
+{
+   FT_Library  library;
+   FT_Face     face;
+   FT_Matrix   matrix;      // transformation matrix
+   FT_Vector   pen;
+
+   FT_UInt glyph_index;
+   FT_Error error;
+
+   FT_Bool use_kerning;
+   FT_UInt previous = 0;
+
+   /* Set up transformation Matrix */
+   matrix.xx = (FT_Fixed)( cos(angle)*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
+   matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
+   matrix.yx = (FT_Fixed)( sin(angle)*0x10000);  //   matrix.yx = - matrix.xy;
+   matrix.yy = (FT_Fixed)( cos(angle)*0x10000);  //   matrix.yy = matrix.xx;
+
+   /* Place starting coordinates in adequate form. */
+   pen.x = x_start*64 ;
+   pen.y =   (int)(y_start/64.0);
+
+   /*Count the length of the string */
+   int num_chars = strlen(text);
+
+   /* Initialize FT Library object */
+   error = FT_Init_FreeType( &library );
+   if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not init Library."<< std::endl; return;}
+
+   /* Initialize FT face object */
+   error = FT_New_Face( library,face_path,0,&face );
+   if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return; }
+
+   /* Set the Char size */
+   error = FT_Set_Char_Size( face,          /* handle to face object           */
+			     0,             /* char_width in 1/64th of points  */
+			     fontsize*64,   /* char_height in 1/64th of points */
+			     100,           /* horizontal device resolution    */
+			     100 );         /* vertical device resolution      */
+
+   /* A way of accesing the glyph directly */
+   FT_GlyphSlot  slot = face->glyph;  // a small shortcut
+
+   /* Does the font file support kerning? */
+   use_kerning = FT_HAS_KERNING( face );
+
+   int n;
+   for ( n = 0; n < num_chars; n++ )
+     {
+	/* Convert character code to glyph index */
+	glyph_index = FT_Get_Char_Index( face, text[n] );
+
+	/* Retrieve kerning distance and move pen position */
+	if ( use_kerning && previous&& glyph_index )
+	  {
+	     FT_Vector  delta;
+	     FT_Get_Kerning( face,
+			     previous,
+			     glyph_index,
+			     ft_kerning_default, //FT_KERNING_DEFAULT,
+			     &delta );
+
+	     /* Transform this kerning distance into rotated space */
+	     pen.x += (int) (((double) delta.x)*cos(angle));
+	     pen.y +=  (int) (((double) delta.x)*( sin(angle)));
+	  }
+
+	/* Set transform */
+	FT_Set_Transform( face, &matrix, &pen );
+
+/*set char size*/
+
+	if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Set char size error." << std::endl; return;};
+
+	/* Retrieve glyph index from character code */
+	glyph_index = FT_Get_Char_Index( face, text[n] );
+
+	/* Load glyph image into the slot (erase previous one) */
+	error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
+	if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;}
+
+	/* Convert to an anti-aliased bitmap */
+	//	error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );
+	error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+	if (error) { std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Render glyph error." << std::endl; return;}
+
+	/* Now, draw to our target surface */
+	my_draw_bitmap( &slot->bitmap,
+			slot->bitmap_left,
+			y_start + slot->bitmap_top,
+			red,
+			green,
+			blue );
+
+	/* Advance to the next position */
+	pen.x += slot->advance.x;
+	pen.y += slot->advance.y;
+
+	/* record current glyph index */
+	previous = glyph_index;
+     }
+
+   /* Free the face and the library objects */
+   FT_Done_Face    ( face );
+   FT_Done_FreeType( library );
+}
+
+void pngwriter::plot_text_utf8( char * face_path, int fontsize, int x_start, int y_start, double angle,  char * text, double red, double green, double blue)
+{
+   FT_Library  library;
+   FT_Face     face;
+   FT_Matrix   matrix;      // transformation matrix
+   FT_Vector   pen;
+
+   FT_UInt glyph_index;
+   FT_Error error;
+
+   FT_Bool use_kerning;
+   FT_UInt previous = 0;
+
+   /* Set up transformation Matrix */
+   matrix.xx = (FT_Fixed)( cos(angle)*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
+   matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
+   matrix.yx = (FT_Fixed)( sin(angle)*0x10000);  //   matrix.yx = - matrix.xy;
+   matrix.yy = (FT_Fixed)( cos(angle)*0x10000);  //   matrix.yy = matrix.xx;
+
+   /* Place starting coordinates in adequate form. */
+   pen.x = x_start*64 ;
+   pen.y = (int)(y_start/64.0);
+
+   /*Count the length of the string */
+   int num_bytes=0;
+   while(text[num_bytes]!=0)
+     {
+	num_bytes++;
+     }
+
+	 /*
+   std::cout << "Num bytes is: "<< num_bytes << std::endl;
+   */
+
+   //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file.
+   long * ucs4text;
+   ucs4text = new long[num_bytes+1];
+
+   unsigned char u,v,w,x,y,z;
+
+   int num_chars=0;
+
+   long iii=0;
+
+   while(iiiglyph;  // a small shortcut
+
+   /* Does the font file support kerning? */
+   use_kerning = FT_HAS_KERNING( face );
+
+   int n;
+   for ( n = 0; n < num_chars; n++ )
+     {
+	/* Convert character code to glyph index */
+	glyph_index = FT_Get_Char_Index( face, ucs4text[n] );
+
+	/* Retrieve kerning distance and move pen position */
+	if ( use_kerning && previous&& glyph_index )
+	  {
+	     FT_Vector  delta;
+	     FT_Get_Kerning( face,
+			     previous,
+			     glyph_index,
+			     ft_kerning_default, //FT_KERNING_DEFAULT,
+			     &delta );
+
+	     /* Transform this kerning distance into rotated space */
+	     pen.x += (int) (((double) delta.x)*cos(angle));
+	     pen.y +=  (int) (((double) delta.x)*( sin(angle)));
+	  }
+
+	/* Set transform */
+	FT_Set_Transform( face, &matrix, &pen );
+
+/*set char size*/
+
+	if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Set char size error." << std::endl; return;};
+
+	/* Retrieve glyph index from character code */
+	glyph_index = FT_Get_Char_Index( face, ucs4text[n] );
+
+	/* Load glyph image into the slot (erase previous one) */
+	error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
+	if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;}
+
+	/* Convert to an anti-aliased bitmap */
+	error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+	if (error) { std::cerr << " PNGwriter::plot_text_utf8 - ERROR **: FreeType: Render glyph error." << std::endl; return;}
+
+	/* Now, draw to our target surface */
+	my_draw_bitmap( &slot->bitmap,
+			slot->bitmap_left,
+			y_start + slot->bitmap_top,
+			red,
+			green,
+			blue );
+
+	/* Advance to the next position */
+	pen.x += slot->advance.x;
+	pen.y += slot->advance.y;
+
+	/* record current glyph index */
+	previous = glyph_index;
+     }
+
+   /* Free the face and the library objects */
+   FT_Done_Face    ( face );
+   FT_Done_FreeType( library );
+
+   delete[] ucs4text;
+}
+
+void pngwriter::plot_text( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue)
+{
+   plot_text( face_path, fontsize, x_start, y_start,  angle,  text,  ((double) red)/65535.0,  ((double) green)/65535.0,  ((double) blue)/65535.0   );
+}
+
+void pngwriter::plot_text_utf8( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue)
+{
+   plot_text_utf8( face_path, fontsize, x_start, y_start,  angle,  text,  ((double) red)/65535.0,  ((double) green)/65535.0,  ((double) blue)/65535.0   );
+}
+
+void pngwriter::my_draw_bitmap( FT_Bitmap * bitmap, int x, int y, double red, double green, double blue)
+{
+   double temp;
+   for(int j=1; jrows+1; j++)
+     {
+	for(int i=1; i< bitmap->width + 1; i++)
+	  {
+	     temp = (double)(bitmap->buffer[(j-1)*bitmap->width + (i-1)] )/255.0;
+
+	     if(temp)
+	       {
+		  this->plot(x + i,
+			     y  - j,
+			     temp*red + (1-temp)*(this->dread(x+i,y-j,1)),
+			     temp*green + (1-temp)*(this->dread(x+i,y-j,2)),
+			     temp*blue + (1-temp)*(this->dread(x+i,y-j,3))
+			     );
+	       }
+	  }
+     }
+}
+
+
+
+//////////// Get text width
+
+//put in freetype section
+
+int pngwriter::get_text_width(char * face_path, int fontsize, char * text)
+{
+   
+   FT_Library  library;
+   FT_Face     face;
+   FT_Matrix   matrix;      // transformation matrix
+   FT_Vector   pen;
+
+   FT_UInt glyph_index;
+   FT_Error error;
+
+   FT_Bool use_kerning;
+   FT_UInt previous = 0;
+
+   /* Set up transformation Matrix */
+   matrix.xx = (FT_Fixed)( 1.0*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
+   matrix.xy = (FT_Fixed)( 0.0*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
+   matrix.yx = (FT_Fixed)( 0.0*0x10000);  //   matrix.yx = - matrix.xy;
+   matrix.yy = (FT_Fixed)( 1.0*0x10000);  //   matrix.yy = matrix.xx;
+
+   /* Place starting coordinates in adequate form. */
+   pen.x = 0;
+   pen.y = 0;
+
+   /*Count the length of the string */
+   int num_chars = strlen(text);
+
+   /* Initialize FT Library object */
+   error = FT_Init_FreeType( &library );
+   if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not init Library."<< std::endl; return 0;}
+
+   /* Initialize FT face object */
+   error = FT_New_Face( library,face_path,0,&face );
+   if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return 0; } else if (error){ std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not find or load font file." << std::endl; return 0; }
+
+   /* Set the Char size */
+   error = FT_Set_Char_Size( face,          /* handle to face object           */
+			     0,             /* char_width in 1/64th of points  */
+			     fontsize*64,   /* char_height in 1/64th of points */
+			     100,           /* horizontal device resolution    */
+			     100 );         /* vertical device resolution      */
+
+   /* A way of accesing the glyph directly */
+   FT_GlyphSlot  slot = face->glyph;  // a small shortcut
+
+   /* Does the font file support kerning? */
+   use_kerning = FT_HAS_KERNING( face );
+
+   int n;
+   for ( n = 0; n < num_chars; n++ )
+     {
+	/* Convert character code to glyph index */
+	glyph_index = FT_Get_Char_Index( face, text[n] );
+
+	/* Retrieve kerning distance and move pen position */
+	if ( use_kerning && previous&& glyph_index )
+	  {
+	     FT_Vector  delta;
+	     FT_Get_Kerning( face,
+			     previous,
+			     glyph_index,
+			     ft_kerning_default, //FT_KERNING_DEFAULT,
+			     &delta );
+
+	     /* Transform this kerning distance into rotated space */
+	     pen.x += (int) ( delta.x);
+	     pen.y +=  0;
+	  }
+
+	/* Set transform */
+	FT_Set_Transform( face, &matrix, &pen );
+
+/*set char size*/
+
+	if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Set char size error." << std::endl; return 0;};
+
+	/* Retrieve glyph index from character code */
+	glyph_index = FT_Get_Char_Index( face, text[n] );
+
+	/* Load glyph image into the slot (erase previous one) */
+	error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
+	if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return 0;}
+
+	/* Convert to an anti-aliased bitmap */
+	//	error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );
+	error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+	if (error) { std::cerr << " PNGwriter::get_text_width - ERROR **: FreeType: Render glyph error." << std::endl; return 0;}
+
+	/* Now, draw to our target surface */
+/*	my_draw_bitmap( &slot->bitmap,
+			slot->bitmap_left,
+			slot->bitmap_top,
+			red,
+			green,
+			blue );
+*/
+	/* Advance to the next position */
+	pen.x += slot->advance.x;
+//	std::cout << ((double) pen.x)/64.0 << std::endl;
+	pen.y += slot->advance.y;
+
+	/* record current glyph index */
+	previous = glyph_index;
+     }
+
+   
+   /* Free the face and the library objects */
+   FT_Done_Face    ( face );
+   FT_Done_FreeType( library );
+   
+   return (int)( ((double)pen.x)/64.0 );
+}
+
+
+int pngwriter::get_text_width_utf8(char * face_path, int fontsize,  char * text)
+{
+   FT_Library  library;
+   FT_Face     face;
+   FT_Matrix   matrix;      // transformation matrix
+   FT_Vector   pen;
+
+   FT_UInt glyph_index;
+   FT_Error error;
+
+   FT_Bool use_kerning;
+   FT_UInt previous = 0;
+
+   /* Set up transformation Matrix */
+   matrix.xx = (FT_Fixed)( 0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
+   matrix.xy = (FT_Fixed)( 0*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
+   matrix.yx = (FT_Fixed)( 0*0x10000);  //   matrix.yx = - matrix.xy;
+   matrix.yy = (FT_Fixed)( 0x10000);  //   matrix.yy = matrix.xx;
+
+   /* Place starting coordinates in adequate form. */
+   pen.x = 0 ;
+   pen.y = 0;
+
+   /*Count the length of the string */
+   int num_bytes=0;
+   while(text[num_bytes]!=0)
+     {
+	num_bytes++;
+     }
+
+	 /*
+   std::cout << "Num bytes is: "<< num_bytes << std::endl;
+   */
+
+   //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file.
+   long * ucs4text;
+   ucs4text = new long[num_bytes+1];
+
+   unsigned char u,v,w,x,y,z;
+
+   int num_chars=0;
+
+   long iii=0;
+
+   while(iiiglyph;  // a small shortcut
+
+   /* Does the font file support kerning? */
+   use_kerning = FT_HAS_KERNING( face );
+
+   int n;
+   for ( n = 0; n < num_chars; n++ )
+     {
+	/* Convert character code to glyph index */
+	glyph_index = FT_Get_Char_Index( face, ucs4text[n] );
+
+	/* Retrieve kerning distance and move pen position */
+	if ( use_kerning && previous&& glyph_index )
+	  {
+	     FT_Vector  delta;
+	     FT_Get_Kerning( face,
+			     previous,
+			     glyph_index,
+			     ft_kerning_default, //FT_KERNING_DEFAULT,
+			     &delta );
+
+	     /* Transform this kerning distance into rotated space */
+	     pen.x += (int) (delta.x);
+	     pen.y +=  0;
+	  }
+
+	/* Set transform */
+	FT_Set_Transform( face, &matrix, &pen );
+
+/*set char size*/
+
+	if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Set char size error." << std::endl; return 0;};
+
+	/* Retrieve glyph index from character code */
+	glyph_index = FT_Get_Char_Index( face, ucs4text[n] );
+
+	/* Load glyph image into the slot (erase previous one) */
+	error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
+	if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return 0;}
+
+	/* Convert to an anti-aliased bitmap */
+	error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+	if (error) { std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **: FreeType: Render glyph error." << std::endl; return 0;}
+
+	/* Now, draw to our target surface */
+/*	my_draw_bitmap( &slot->bitmap,
+			slot->bitmap_left,
+			y_start + slot->bitmap_top,
+			red,
+			green,
+			blue );
+*/
+	/* Advance to the next position */
+	pen.x += slot->advance.x;
+	pen.y += slot->advance.y;
+
+	/* record current glyph index */
+	previous = glyph_index;
+     }
+
+   /* Free the face and the library objects */
+   FT_Done_Face    ( face );
+   FT_Done_FreeType( library );
+
+   delete[] ucs4text;
+ 
+   return (int) (((double) pen.x)/64.0);
+}
+
+///////////////
+#endif
+#ifdef NO_FREETYPE
+
+void pngwriter::plot_text( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue)
+{
+   std::cerr << " PNGwriter::plot_text - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return;
+}
+
+void pngwriter::plot_text( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue)
+{
+   std::cerr << " PNGwriter::plot_text - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return;
+
+}
+
+void pngwriter::plot_text_utf8( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue)
+{
+   std::cerr << " PNGwriter::plot_text_utf8 - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return;
+}
+
+void pngwriter::plot_text_utf8( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue)
+{
+   std::cerr << " PNGwriter::plot_text_utf8 - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return;
+}
+
+//////////// Get text width
+int pngwriter::get_text_width(char * face_path, int fontsize, char * text)
+{
+   std::cerr << " PNGwriter::get_text_width - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return 0;
+}
+
+
+int pngwriter::get_text_width_utf8(char * face_path, int fontsize,  char * text)
+{
+   std::cerr << " PNGwriter::get_text_width_utf8 - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return 0;
+}
+
+///////////////
+#endif
+
+/////////////////////////////////////
+int pngwriter::bilinear_interpolation_read(double x, double y, int colour)
+{
+
+   int inty, intx;
+   inty = (int) ceil(y);
+   intx = (int) ceil(x);
+
+   //inty = (int) floor(y) +1;
+   // intx = (int) floor(x) +1;
+   //
+   bool attop, atright;
+   attop = inty==this->height_;
+   atright =  intx==this->width_;
+/*
+   if( intx==this->width_ +1)
+     {
+	intx--;
+	//	std::cout << "intx--" << std::endl;
+
+     }
+  */
+   /*
+   if(inty == this->height_ +1)
+     {
+	inty--;
+	//	std::cout << "inty--" << std::endl;
+     }
+   */
+
+   if( (!attop)&&(!atright) )
+     {
+
+	double f,g,f1,g1;
+	f = 1.0 + x - ((double) intx);
+	g = 1.0 + y - ((double) inty);
+	f1 = 1.0 - f;
+	g1 = 1.0 - g;
+
+	return (int) (
+		      f1*g1*this->read(intx, inty,colour)
+		      + f*g1*this->read(intx+1,inty,colour)
+		      +f1*g*this->read(intx,inty+1,colour)
+		      + f*g*(this->read(intx+1,inty+1,colour))
+		      );
+     }
+
+   if( (atright)&&(!attop))
+     {
+
+	double f,g,f1,g1;
+	f = 1.0 + x - ((double) intx);
+	g = 1.0 + y - ((double) inty);
+	f1 = 1.0 - f;
+	g1 = 1.0 - g;
+
+	return (int) (
+		      f1*g1*this->read(intx, inty,colour)
+		      + f*g1*( 2*(this->read(intx,inty,colour)) - (this->read(intx-1,inty,colour)) )
+		      +f1*g*this->read(intx,inty+1,colour)
+		      + f*g*(2*(this->read(intx,inty+1,colour)) - (this->read(intx-1,inty+1,colour)))
+		      );
+     }
+
+   if((attop)&&(!atright))
+     {
+	double f,g,f1,g1;
+	f = 1.0 + x - ((double) intx);
+	g = 1.0 + y - ((double) inty);
+	f1 = 1.0 - f;
+	g1 = 1.0 - g;
+
+	return (int) (
+		      f1*g1*this->read(intx, inty,colour)
+		      + f*g1*this->read(intx+1,inty,colour)
+		      +f1*g*( 2*(this->read(intx,inty,colour))  - this->read(intx,inty-1,colour) )
+		      + f*g*( 2*(this->read(intx+1,inty,colour))  - this->read(intx+1,inty-1,colour))
+		      );
+     }
+
+   double f,g,f1,g1;
+   f = 1.0 + x - ((double) intx);
+   g = 1.0 + y - ((double) inty);
+   f1 = 1.0 - f;
+   g1 = 1.0 - g;
+
+   return (int) (
+		 f1*g1*this->read(intx, inty,colour)
+		 + f*g1*( 2*(this->read(intx,inty,colour)) - (this->read(intx-1,inty,colour)) )
+		 +f1*g*( 2*(this->read(intx,inty,colour))  - this->read(intx,inty-1,colour) )
+		 + f*g*( 2*( 2*(this->read(intx,inty,colour)) - (this->read(intx-1,inty,colour)) )  - ( 2*(this->read(intx,inty-1,colour)) - (this->read(intx-1,inty-1,colour)) ))
+		 );
+
+   /*
+    return (int) (
+    f1*g1*this->read(intx, inty,colour)
+    + f*g1*this->read(intx+1,inty,colour)
+    +f1*g*this->read(intx,inty+1,colour)
+    + f*g*this->read(intx+1, inty+1,colour)
+    );
+    * */
+
+};
+
+double pngwriter::bilinear_interpolation_dread(double x, double y, int colour)
+{
+   return double(this->bilinear_interpolation_read(x,y,colour))/65535.0;
+};
+
+void pngwriter::plot_blend(int x, int y, double opacity, int red, int green, int blue)
+{
+   this->plot(x, y,
+	      (int)(  opacity*red   +  this->read(x,y,1)*(1.0-opacity)),
+	      (int)( opacity*green +  this->read(x,y,2)*(1.0-opacity)),
+	      (int)( opacity*blue  +  this->read(x,y,3)*(1.0-opacity))
+	      );
+};
+
+void pngwriter::plot_blend(int x, int y, double opacity, double red, double green, double blue)
+{
+   this->plot_blend(x, y, opacity, (int)  (65535*red), (int)  (65535*green),  (int)  (65535*blue));
+};
+
+void pngwriter::invert(void)
+{
+   //   int temp1, temp2, temp3;
+   double temp11, temp22, temp33;
+
+   for(int jjj = 1; jjj <= (this->height_); jjj++)
+     {
+	for(int iii = 1; iii <= (this->width_); iii++)
+	  {
+	     /*	     temp11 = (this->read(iii,jjj,1));
+	      temp22 = (this->read(iii,jjj,2));
+	      temp33 = (this->read(iii,jjj,3));
+	      *
+	      this->plot(iii,jjj,
+	      ((double)(65535 - temp11))/65535.0,
+	      ((double)(65535 - temp22))/65535.0,
+	      ((double)(65535 - temp33))/65535.0
+	      );
+	      *
+	      */
+	     temp11 = (this->read(iii,jjj,1));
+	     temp22 = (this->read(iii,jjj,2));
+	     temp33 = (this->read(iii,jjj,3));
+
+	     this->plot(iii,jjj,
+			(int)(65535 - temp11),
+			(int)(65535 - temp22),
+			(int)(65535 - temp33)
+			);
+
+	  }
+     }
+}
+
+void pngwriter::resize(int width, int height)
+{
+
+   for (int jjj = 0; jjj < height_; jjj++) free(graph_[jjj]);
+   free(graph_);
+
+   width_ = width;
+   height_ = height;
+   backgroundcolour_ = 0;
+
+   graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::resize - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   for (int kkkk = 0; kkkk < height_; kkkk++)
+     {
+	graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte));
+	if(graph_[kkkk] == NULL)
+	  {
+	     std::cerr << " PNGwriter::resize - ERROR **:  Not able to allocate memory for image." << std::endl;
+	  }
+     }
+
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::resize - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   int tempindex;
+   for(int hhh = 0; hhhdread(xstart,ystart,1) != boundary_red) ||
+	(this->dread(xstart,ystart,2) != boundary_green) ||
+	(this->dread(xstart,ystart,3) != boundary_blue)
+	)
+       &&
+       (
+	(this->dread(xstart,ystart,1) != fill_red) ||
+	(this->dread(xstart,ystart,2) != fill_green) ||
+	(this->dread(xstart,ystart,3) != fill_blue)
+	)
+       &&
+       (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_)
+       )
+     {
+	this->plot(xstart, ystart, fill_red, fill_green, fill_blue);
+	boundary_fill(xstart+1,  ystart,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
+	boundary_fill(xstart,  ystart+1,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
+	boundary_fill(xstart,  ystart-1,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
+	boundary_fill(xstart-1,  ystart,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
+     }
+}
+
+//no int version needed
+void pngwriter::flood_fill_internal(int xstart, int ystart,  double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue)
+{
+   if( (
+	(this->dread(xstart,ystart,1) == start_red) &&
+	(this->dread(xstart,ystart,2) == start_green) &&
+	(this->dread(xstart,ystart,3) == start_blue)
+	)
+       &&
+       (
+	(this->dread(xstart,ystart,1) != fill_red) ||
+	(this->dread(xstart,ystart,2) != fill_green) ||
+	(this->dread(xstart,ystart,3) != fill_blue)
+	)
+       &&
+       (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_)
+       )
+     {
+	this->plot(xstart, ystart, fill_red, fill_green, fill_blue);
+	flood_fill_internal(  xstart+1,  ystart,   start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
+	flood_fill_internal(  xstart-1,  ystart,   start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
+	flood_fill_internal(  xstart,  ystart+1,   start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
+	flood_fill_internal(  xstart,  ystart-1,   start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
+     }
+
+}
+
+//int version
+void pngwriter::boundary_fill(int xstart, int ystart, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue)
+{
+
+   this->boundary_fill( xstart, ystart,
+			((double) boundary_red)/65535.0,
+			((double) boundary_green)/65535.0,
+			((double) boundary_blue)/65535.0,
+			((double) fill_red)/65535.0,
+			((double) fill_green)/65535.0,
+			((double) fill_blue)/65535.0
+			);
+}
+
+void pngwriter::flood_fill(int xstart, int ystart, double fill_red, double fill_green, double fill_blue)
+{
+   flood_fill_internal(  xstart,  ystart,  this->dread(xstart,ystart,1),this->dread(xstart,ystart,2),this->dread(xstart,ystart,3),   fill_red,  fill_green, fill_blue);
+}
+
+//int version
+void pngwriter::flood_fill(int xstart, int ystart, int fill_red, int fill_green, int fill_blue)
+{
+   this->flood_fill( xstart,  ystart,
+		     ((double)  fill_red)/65535.0,
+		     ((double) fill_green)/65535.0,
+		     ((double)  fill_blue)/65535.0
+		     );
+}
+
+void pngwriter::polygon( int * points, int number_of_points, double red, double green, double blue)
+{
+   if( (number_of_points<1)||(points ==NULL))
+     {
+	std::cerr << " PNGwriter::polygon - ERROR **:  Number of points is zero or negative, or array is NULL." << std::endl;
+	return;
+     }
+
+   for(int k=0;k< number_of_points-1; k++)
+     {
+	this->line(points[2*k],points[2*k+1],points[2*k+2],points[2*k+3], red, green, blue);
+     }
+}
+
+//int version
+void pngwriter::polygon( int * points, int number_of_points, int red, int green, int blue)
+{
+   this->polygon(points, number_of_points,
+		 ((double) red)/65535.0,
+		 ((double) green)/65535.0,
+		 ((double) blue)/65535.0
+		 );
+}
+
+void pngwriter::plotCMYK(int x, int y, double cyan, double magenta, double yellow, double black)
+{
+/*CMYK to RGB:
+ *  -----------
+ *  red   = 255 - minimum(255,((cyan/255)    * (255 - black) + black))
+ *  green = 255 - minimum(255,((magenta/255) * (255 - black) + black))
+ *  blue  = 255 - minimum(255,((yellow/255)  * (255 - black) + black))
+ * */
+
+   if(cyan<0.0)
+     {
+	cyan = 0.0;
+     }
+   if(magenta<0.0)
+     {
+	magenta = 0.0;
+     }
+   if(yellow<0.0)
+     {
+	yellow = 0.0;
+     }
+   if(black<0.0)
+     {
+	black = 0.0;
+     }
+
+   if(cyan>1.0)
+     {
+	cyan = 1.0;
+     }
+   if(magenta>1.0)
+     {
+	magenta = 1.0;
+     }
+   if(yellow>1.0)
+     {
+	yellow = 1.0;
+     }
+   if(black>1.0)
+     {
+	black = 1.0;
+     }
+
+   double  red, green, blue, minr, ming, minb, iblack;
+
+   iblack = 1.0 - black;
+
+   minr = 1.0;
+   ming = 1.0;
+   minb = 1.0;
+
+   if( (cyan*iblack + black)<1.0 )
+     {
+	minr = cyan*iblack + black;
+     }
+
+   if( (magenta*iblack + black)<1.0 )
+     {
+	ming = magenta*iblack + black;
+     }
+
+   if( (yellow*iblack + black)<1.0 )
+     {
+	minb = yellow*iblack + black;
+     }
+
+   red = 1.0 - minr;
+   green = 1.0 - ming;
+   blue = 1.0 - minb;
+
+   this->plot(x,y,red, green, blue);
+
+}
+
+//int version
+void pngwriter::plotCMYK(int x, int y, int cyan, int magenta, int yellow, int black)
+{
+   this->plotCMYK( x, y,
+		   ((double) cyan)/65535.0,
+		   ((double) magenta)/65535.0,
+		   ((double) yellow)/65535.0,
+		   ((double) black)/65535.0
+		   );
+}
+
+double pngwriter::dreadCMYK(int x, int y, int colour)
+{
+/*
+ * Black   = minimum(1-Red,1-Green,1-Blue)
+ *     Cyan    = (1-Red-Black)/(1-Black)
+ *     Magenta = (1-Green-Black)/(1-Black)
+ *     Yellow  = (1-Blue-Black)/(1-Black)
+ *
+ * */
+   if((colour !=1)&&(colour !=2)&&(colour !=3)&&(colour !=4))
+     {
+	std::cerr << " PNGwriter::dreadCMYK - WARNING **: Invalid argument: should be 1, 2, 3 or 4, is " << colour << std::endl;
+	return 0;
+     }
+
+   double black, red, green, blue, ired, igreen, iblue, iblack;
+   //add error detection here
+   // not much to detect, really
+   red = this->dread(x, y, 1);
+   green = this->dread(x, y, 2);
+   blue = this->dread(x, y, 3);
+
+   ired = 1.0 - red;
+   igreen = 1.0 - green;
+   iblue = 1.0 - blue;
+
+   black = ired;
+
+   //black is the mimimum of inverse RGB colours, and if they are all equal, it is the inverse of red.
+   if( (igreendread(x, y, 1);
+   green = this->dread(x, y, 2);
+   blue = this->dread(x, y, 3);
+
+   ired = 1.0 - red;
+   igreen = 1.0 - green;
+   iblue = 1.0 - blue;
+
+   black = ired;
+
+   //black is the mimimum of inverse RGB colours, and if they are all equal, it is the inverse of red.
+   if( (igreenbilinear_interpolation_read(readx, ready, 1);
+	     green = this->bilinear_interpolation_read(readx, ready, 2);
+	     blue = this->bilinear_interpolation_read(readx, ready, 3);
+	     temp.plot(x, y, red, green, blue);
+
+	  }
+     }
+
+   // From here on, the process is the same for all scale functions.
+   //Get data out of temp and into this's storage.
+
+   //Resize this instance
+   // Delete current storage.
+   for (int jjj = 0; jjj < height_; jjj++) free(graph_[jjj]);
+   free(graph_);
+
+   //New image will have bit depth 16, regardless of original bit depth.
+   bit_depth_ = 16;
+
+   // New width and height will be the scaled width and height
+   width_ = scaledw;
+   height_ = scaledh;
+   backgroundcolour_ = 0;
+
+   graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::scale_k - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   for (int kkkk = 0; kkkk < height_; kkkk++)
+     {
+	graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte));
+	if(graph_[kkkk] == NULL)
+	  {
+	     std::cerr << " PNGwriter::scale_k - ERROR **:  Not able to allocate memory for image." << std::endl;
+	  }
+     }
+
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::scale_k - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   //This instance now has a new, resized storage space.
+
+   //Copy the temp date into this's storage.
+   int tempindex;
+   for(int hhh = 0; hhhbilinear_interpolation_read(readx, ready, 1);
+	     green = this->bilinear_interpolation_read(readx, ready, 2);
+	     blue = this->bilinear_interpolation_read(readx, ready, 3);
+	     temp.plot(x, y, red, green, blue);
+
+	  }
+     }
+   // From here on, the process is the same for all scale functions.
+   //Get data out of temp and into this's storage.
+
+   //Resize this instance
+   // Delete current storage.
+   for (int jjj = 0; jjj < height_; jjj++) free(graph_[jjj]);
+   free(graph_);
+
+   //New image will have bit depth 16, regardless of original bit depth.
+   bit_depth_ = 16;
+
+   // New width and height will be the scaled width and height
+   width_ = scaledw;
+   height_ = scaledh;
+   backgroundcolour_ = 0;
+
+   graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::scale_kxky - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   for (int kkkk = 0; kkkk < height_; kkkk++)
+     {
+	graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte));
+	if(graph_[kkkk] == NULL)
+	  {
+	     std::cerr << " PNGwriter::scale_kxky - ERROR **:  Not able to allocate memory for image." << std::endl;
+	  }
+     }
+
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::scale_kxky - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   //This instance now has a new, resized storage space.
+
+   //Copy the temp date into this's storage.
+   int tempindex;
+   for(int hhh = 0; hhhbilinear_interpolation_read(readx, ready, 1);
+	     green = this->bilinear_interpolation_read(readx, ready, 2);
+	     blue = this->bilinear_interpolation_read(readx, ready, 3);
+	     temp.plot(x, y, red, green, blue);
+
+	  }
+     }
+
+   // From here on, the process is the same for all scale functions.
+   //Get data out of temp and into this's storage.
+
+   //Resize this instance
+   // Delete current storage.
+   for (int jjj = 0; jjj < height_; jjj++) free(graph_[jjj]);
+   free(graph_);
+
+   //New image will have bit depth 16, regardless of original bit depth.
+   bit_depth_ = 16;
+
+   // New width and height will be the scaled width and height
+   width_ = finalwidth;
+   height_ = finalheight;
+   backgroundcolour_ = 0;
+
+   graph_ = (png_bytepp)malloc(height_ * sizeof(png_bytep));
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::scale_wh - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   for (int kkkk = 0; kkkk < height_; kkkk++)
+     {
+	graph_[kkkk] = (png_bytep)malloc(6*width_ * sizeof(png_byte));
+	if(graph_[kkkk] == NULL)
+	  {
+	     std::cerr << " PNGwriter::scale_wh - ERROR **:  Not able to allocate memory for image." << std::endl;
+	  }
+     }
+
+   if(graph_ == NULL)
+     {
+	std::cerr << " PNGwriter::scale_wh - ERROR **:  Not able to allocate memory for image." << std::endl;
+     }
+
+   //This instance now has a new, resized storage space.
+
+   //Copy the temp date into this's storage.
+   int tempindex;
+   for(int hhh = 0; hhhplot_blend(xfrom,yfrom,opacity, red,green,blue);
+
+   if (dx > dy)
+     {
+	int fraction = dy - (dx >> 1);
+
+	while (xfrom != xto)
+	  {
+	     if (fraction >= 0)
+	       {
+		  yfrom += stepy;
+		  fraction -= dx;
+	       }
+	     xfrom += stepx;
+	     fraction += dy;
+	     this->plot_blend(xfrom,yfrom,opacity, red,green,blue);
+	  }
+     }
+   else
+     {
+	int fraction = dx - (dy >> 1);
+	while (yfrom != yto)
+	  {
+	     if (fraction >= 0)
+	       {
+		  xfrom += stepx;
+		  fraction -= dy;
+	       }
+	     yfrom += stepy;
+	     fraction += dx;
+	     this->plot_blend(xfrom,yfrom, opacity, red,green,blue);
+	  }
+     }
+
+}
+
+void pngwriter::line_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double  blue)
+{
+   this->line_blend( xfrom,
+		     yfrom,
+		     xto,
+		     yto,
+		     opacity,
+		     int (red*65535),
+		     int (green*65535),
+		     int (blue*65535)
+		     );
+
+}
+
+void pngwriter::square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int  blue)
+{
+   this->line_blend(xfrom, yfrom, xfrom, yto, opacity, red, green, blue);
+   this->line_blend(xto, yfrom, xto, yto, opacity, red, green, blue);
+   this->line_blend(xfrom, yfrom, xto, yfrom, opacity, red, green, blue);
+   this->line_blend(xfrom, yto, xto, yto, opacity,  red, green, blue);
+}
+
+void pngwriter::square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double  blue)
+{
+   this->square_blend( xfrom,  yfrom,  xto,  yto, opacity, int(red*65535), int(green*65535), int(blue*65535));
+}
+
+void pngwriter::filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int  blue)
+{
+   for(int caca = xfrom; caca line_blend(caca, yfrom, caca, yto, opacity, red, green, blue);
+     }
+
+}
+
+void pngwriter::filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double  blue)
+{
+   this->filledsquare_blend( xfrom,  yfrom,  xto,  yto, opacity, int(red*65535), int(green*65535), int(blue*65535));
+}
+
+void pngwriter::circle_aux_blend(int xcentre, int ycentre, int x, int y, double opacity, int red, int green, int blue)
+{
+   if (x == 0)
+     {
+	this->plot_blend( xcentre, ycentre + y, opacity, red, green, blue);
+	this->plot_blend( xcentre, ycentre - y, opacity, red, green, blue);
+	this->plot_blend( xcentre + y, ycentre, opacity, red, green, blue);
+	this->plot_blend( xcentre - y, ycentre, opacity, red, green, blue);
+     }
+   else
+     if (x == y)
+       {
+	  this->plot_blend( xcentre + x, ycentre + y, opacity, red, green, blue);
+	  this->plot_blend( xcentre - x, ycentre + y, opacity, red, green, blue);
+	  this->plot_blend( xcentre + x, ycentre - y, opacity, red, green, blue);
+	  this->plot_blend( xcentre - x, ycentre - y, opacity, red, green, blue);
+       }
+   else
+     if (x < y)
+       {
+	  this->plot_blend( xcentre + x, ycentre + y, opacity, red, green, blue);
+	  this->plot_blend( xcentre - x, ycentre + y, opacity, red, green, blue);
+	  this->plot_blend( xcentre + x, ycentre - y, opacity, red, green, blue);
+	  this->plot_blend( xcentre - x, ycentre - y, opacity, red, green, blue);
+	  this->plot_blend( xcentre + y, ycentre + x, opacity, red, green, blue);
+	  this->plot_blend( xcentre - y, ycentre + x, opacity, red, green, blue);
+	  this->plot_blend( xcentre + y, ycentre - x, opacity, red, green, blue);
+	  this->plot_blend( xcentre - y, ycentre - x, opacity, red, green, blue);
+       }
+
+}
+//
+
+void pngwriter::circle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue)
+{
+   int x = 0;
+   int y = radius;
+   int p = (5 - radius*4)/4;
+
+   circle_aux_blend(xcentre, ycentre, x, y, opacity, red, green, blue);
+   while (x < y)
+     {
+	x++;
+	if (p < 0)
+	  {
+	     p += 2*x+1;
+	  }
+	else
+	  {
+	     y--;
+	     p += 2*(x-y)+1;
+	  }
+	circle_aux_blend(xcentre, ycentre, x, y, opacity, red, green, blue);
+     }
+
+}
+
+void pngwriter::circle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue)
+{
+   this->circle_blend(xcentre,ycentre,radius, opacity,  int(red*65535), int(green*65535), int(blue*65535));
+}
+
+void pngwriter::filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue)
+{
+   for(int jjj = ycentre-radius; jjj< ycentre+radius+1; jjj++)
+     {
+	this->line_blend(xcentre - int(sqrt((double)(radius*radius) - (-ycentre + jjj)*(-ycentre + jjj ))), jjj,
+			 xcentre + int(sqrt((double)(radius*radius) - (-ycentre + jjj)*(-ycentre + jjj ))),jjj, opacity, red,green,blue);
+     }
+
+}
+
+void pngwriter::filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue)
+{
+   this->filledcircle_blend( xcentre, ycentre,  radius, opacity, int(red*65535), int(green*65535), int(blue*65535));
+}
+
+void pngwriter::bezier_blend(  int startPtX, int startPtY,
+			       int startControlX, int startControlY,
+			       int endPtX, int endPtY,
+			       int endControlX, int endControlY,
+			       double opacity,
+			       double red, double green, double blue)
+{
+
+   double cx = 3.0*(startControlX - startPtX);
+   double bx = 3.0*(endControlX - startControlX) - cx;
+   double ax = double(endPtX - startPtX - cx - bx);
+
+   double cy = 3.0*(startControlY - startPtY);
+   double by = 3.0*(endControlY - startControlY) - cy;
+   double ay = double(endPtY - startPtY - cy - by);
+
+   double x,y,newx,newy;
+   x = startPtX;
+   y = startPtY;
+
+   for(double t = 0.0; t<=1.005; t += 0.005)
+     {
+	newx = startPtX + t*(double(cx) + t*(double(bx) + t*(double(ax))));
+	newy = startPtY + t*(double(cy) + t*(double(by) + t*(double(ay))));
+	this->line_blend(int(x),int(y),int(newx),int(newy),opacity, red,green,blue);
+	x = newx;
+	y = newy;
+     }
+}
+
+void pngwriter::bezier_blend(  int startPtX, int startPtY,
+			       int startControlX, int startControlY,
+			       int endPtX, int endPtY,
+			       int endControlX, int endControlY,
+			       double opacity,
+			       int red, int green, int blue)
+{
+   this->bezier_blend(   startPtX,  startPtY,
+			 startControlX, startControlY,
+			 endPtX, endPtY,
+			 endControlX,  endControlY,
+			 opacity,
+			 double(red)/65535.0,  double(green)/65535.0,  double(blue)/65535.0);
+
+}
+
+/////////////////////////////
+#ifndef NO_FREETYPE
+
+// Freetype-based text rendering functions.
+///////////////////////////////////////////
+void pngwriter::plot_text_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue)
+{
+   FT_Library  library;
+   FT_Face     face;
+   FT_Matrix   matrix;      // transformation matrix
+   FT_Vector   pen;
+
+   FT_UInt glyph_index;
+   FT_Error error;
+
+   FT_Bool use_kerning;
+   FT_UInt previous = 0;
+
+   /* Set up transformation Matrix */
+   matrix.xx = (FT_Fixed)( cos(angle)*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
+   matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
+   matrix.yx = (FT_Fixed)( sin(angle)*0x10000);  //   matrix.yx = - matrix.xy;
+   matrix.yy = (FT_Fixed)( cos(angle)*0x10000);  //   matrix.yy = matrix.xx;
+
+   /* Place starting coordinates in adequate form. */
+   pen.x = x_start*64 ;
+   pen.y =   (int)(y_start/64.0);
+
+   /*Count the length of the string */
+   int num_chars = strlen(text);
+
+   /* Initialize FT Library object */
+   error = FT_Init_FreeType( &library );
+   if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Could not init Library."<< std::endl; return;}
+
+   /* Initialize FT face object */
+   error = FT_New_Face( library,face_path,0,&face );
+   if ( error == FT_Err_Unknown_File_Format ) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Font was opened, but type not supported."<< std::endl; return; } else if (error){ std::cerr << " PNGwriter::plot_text - ERROR **: FreeType: Could not find or load font file."<< std::endl; return; }
+
+   /* Set the Char size */
+   error = FT_Set_Char_Size( face,          /* handle to face object           */
+			     0,             /* char_width in 1/64th of points  */
+			     fontsize*64,   /* char_height in 1/64th of points */
+			     100,           /* horizontal device resolution    */
+			     100 );         /* vertical device resolution      */
+
+   /* A way of accesing the glyph directly */
+   FT_GlyphSlot  slot = face->glyph;  // a small shortcut
+
+   /* Does the font file support kerning? */
+   use_kerning = FT_HAS_KERNING( face );
+
+   int n;
+   for ( n = 0; n < num_chars; n++ )
+     {
+	/* Convert character code to glyph index */
+	glyph_index = FT_Get_Char_Index( face, text[n] );
+
+	/* Retrieve kerning distance and move pen position */
+	if ( use_kerning && previous&& glyph_index )
+	  {
+	     FT_Vector  delta;
+	     FT_Get_Kerning( face,
+			     previous,
+			     glyph_index,
+			     ft_kerning_default, //FT_KERNING_DEFAULT,
+			     &delta );
+
+	     /* Transform this kerning distance into rotated space */
+	     pen.x += (int) (((double) delta.x)*cos(angle));
+	     pen.y +=  (int) (((double) delta.x)*( sin(angle)));
+	  }
+
+	/* Set transform */
+	FT_Set_Transform( face, &matrix, &pen );
+
+/*set char size*/
+
+	if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Set char size error." << std::endl; return;};
+
+	/* Retrieve glyph index from character code */
+	glyph_index = FT_Get_Char_Index( face, text[n] );
+
+	/* Load glyph image into the slot (erase previous one) */
+	error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
+	if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;}
+
+	/* Convert to an anti-aliased bitmap */
+	//	error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );
+	error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+	if (error) { std::cerr << " PNGwriter::plot_text_blend - ERROR **: FreeType: Render glyph error." << std::endl; return;}
+
+	/* Now, draw to our target surface */
+	my_draw_bitmap_blend( &slot->bitmap,
+			      slot->bitmap_left,
+			      y_start + slot->bitmap_top,
+			      opacity,
+			      red,
+			      green,
+			      blue );
+
+	/* Advance to the next position */
+	pen.x += slot->advance.x;
+	pen.y += slot->advance.y;
+
+	/* record current glyph index */
+	previous = glyph_index;
+     }
+
+   /* Free the face and the library objects */
+   FT_Done_Face    ( face );
+   FT_Done_FreeType( library );
+}
+
+void pngwriter::plot_text_utf8_blend( char * face_path, int fontsize, int x_start, int y_start, double angle,  char * text, double opacity, double red, double green, double blue)
+{
+   FT_Library  library;
+   FT_Face     face;
+   FT_Matrix   matrix;      // transformation matrix
+   FT_Vector   pen;
+
+   FT_UInt glyph_index;
+   FT_Error error;
+
+   FT_Bool use_kerning;
+   FT_UInt previous = 0;
+
+   /* Set up transformation Matrix */
+   matrix.xx = (FT_Fixed)( cos(angle)*0x10000);   /* It would make more sense to do this (below), but, bizzarely, */
+   matrix.xy = (FT_Fixed)(-sin(angle)*0x10000);   /* if one does, FT_Load_Glyph fails consistently.               */
+   matrix.yx = (FT_Fixed)( sin(angle)*0x10000);  //   matrix.yx = - matrix.xy;
+   matrix.yy = (FT_Fixed)( cos(angle)*0x10000);  //   matrix.yy = matrix.xx;
+
+   /* Place starting coordinates in adequate form. */
+   pen.x = x_start*64 ;
+   pen.y = (int)(y_start/64.0);
+
+   /*Count the length of the string */
+   int num_bytes=0;
+   while(text[num_bytes]!=0)
+     {
+	num_bytes++;
+     }
+
+	 /*
+   std::cout << "Num bytes is: "<< num_bytes << std::endl;
+   */
+
+   //The array of ucs4 glyph indexes, which will by at most the number of bytes in the utf-8 file.
+   long * ucs4text;
+   ucs4text = new long[num_bytes+1];
+
+   unsigned char u,v,w,x,y,z;
+
+   int num_chars=0;
+
+   long iii=0;
+
+   while(iiiglyph;  // a small shortcut
+
+   /* Does the font file support kerning? */
+   use_kerning = FT_HAS_KERNING( face );
+
+   int n;
+   for ( n = 0; n < num_chars; n++ )
+     {
+	/* Convert character code to glyph index */
+	glyph_index = FT_Get_Char_Index( face, ucs4text[n] );
+
+	/* Retrieve kerning distance and move pen position */
+	if ( use_kerning && previous&& glyph_index )
+	  {
+	     FT_Vector  delta;
+	     FT_Get_Kerning( face,
+			     previous,
+			     glyph_index,
+			     ft_kerning_default, //FT_KERNING_DEFAULT,
+			     &delta );
+
+	     /* Transform this kerning distance into rotated space */
+	     pen.x += (int) (((double) delta.x)*cos(angle));
+	     pen.y +=  (int) (((double) delta.x)*( sin(angle)));
+	  }
+
+	/* Set transform */
+	FT_Set_Transform( face, &matrix, &pen );
+
+/*set char size*/
+
+	if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Set char size error." << std::endl; return;};
+
+	/* Retrieve glyph index from character code */
+	glyph_index = FT_Get_Char_Index( face, ucs4text[n] );
+
+	/* Load glyph image into the slot (erase previous one) */
+	error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
+	if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Could not load glyph (in loop). (FreeType error "<< std::hex << error <<")." << std::endl; return;}
+
+	/* Convert to an anti-aliased bitmap */
+	error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+	if (error) { std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **: FreeType: Render glyph error." << std::endl; return;}
+
+	/* Now, draw to our target surface */
+	my_draw_bitmap_blend( &slot->bitmap,
+			      slot->bitmap_left,
+			      y_start + slot->bitmap_top,
+			      opacity,
+			      red,
+			      green,
+			      blue );
+
+	/* Advance to the next position */
+	pen.x += slot->advance.x;
+	pen.y += slot->advance.y;
+
+	/* record current glyph index */
+	previous = glyph_index;
+     }
+
+   /* Free the face and the library objects */
+   FT_Done_Face    ( face );
+   FT_Done_FreeType( library );
+
+   delete[] ucs4text;
+}
+
+void pngwriter::plot_text_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue)
+{
+   plot_text_blend( face_path, fontsize, x_start, y_start,  angle,  text, opacity,   ((double) red)/65535.0,  ((double) green)/65535.0,  ((double) blue)/65535.0   );
+}
+
+void pngwriter::plot_text_utf8_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity,  int red, int green, int blue)
+{
+   plot_text_utf8_blend( face_path, fontsize, x_start, y_start,  angle,  text, opacity,  ((double) red)/65535.0,  ((double) green)/65535.0,  ((double) blue)/65535.0   );
+}
+
+void pngwriter::my_draw_bitmap_blend( FT_Bitmap * bitmap, int x, int y, double opacity, double red, double green, double blue)
+{
+   double temp;
+   for(int j=1; jrows+1; j++)
+     {
+	for(int i=1; i< bitmap->width + 1; i++)
+	  {
+	     temp = (double)(bitmap->buffer[(j-1)*bitmap->width + (i-1)] )/255.0;
+
+	     if(temp)
+	       {
+		  this->plot_blend(x + i,
+				   y  - j,
+				   opacity,
+				   temp*red + (1-temp)*(this->dread(x+i,y-j,1)),
+				   temp*green + (1-temp)*(this->dread(x+i,y-j,2)),
+				   temp*blue + (1-temp)*(this->dread(x+i,y-j,3))
+				   );
+	       }
+	  }
+     }
+}
+
+#endif
+#ifdef NO_FREETYPE
+
+void pngwriter::plot_text_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue)
+{
+   std::cerr << " PNGwriter::plot_text_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return;
+}
+
+void pngwriter::plot_text_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity,  double red, double green, double blue)
+{
+   std::cerr << " PNGwriter::plot_text_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return;
+
+}
+
+void pngwriter::plot_text_utf8_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity,  int red, int green, int blue)
+{
+   std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return;
+}
+
+void pngwriter::plot_text_utf8_blend( char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue)
+{
+   std::cerr << " PNGwriter::plot_text_utf8_blend - ERROR **:  PNGwriter was compiled without Freetype support! Recompile PNGwriter with Freetype support (once you have Freetype installed, that is. Websites: www.freetype.org and pngwriter.sourceforge.net)." << std::endl;
+   return;
+}
+
+#endif
+
+///////////////////////////
+
+void pngwriter::boundary_fill_blend(int xstart, int ystart, double opacity, double boundary_red,double boundary_green,double boundary_blue,double fill_red, double fill_green, double fill_blue)
+{
+   if( (
+	(this->dread(xstart,ystart,1) != boundary_red) ||
+	(this->dread(xstart,ystart,2) != boundary_green) ||
+	(this->dread(xstart,ystart,3) != boundary_blue)
+	)
+       &&
+       (
+	(this->dread(xstart,ystart,1) != fill_red) ||
+	(this->dread(xstart,ystart,2) != fill_green) ||
+	(this->dread(xstart,ystart,3) != fill_blue)
+	)
+       &&
+       (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_)
+       )
+     {
+	this->plot_blend(xstart, ystart, opacity,  fill_red, fill_green, fill_blue);
+	boundary_fill_blend(xstart+1,  ystart, opacity,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
+	boundary_fill_blend(xstart,  ystart+1, opacity,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
+	boundary_fill_blend(xstart,  ystart-1, opacity,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
+	boundary_fill_blend(xstart-1,  ystart, opacity,  boundary_red, boundary_green, boundary_blue, fill_red,  fill_green,  fill_blue) ;
+     }
+}
+
+//no int version needed
+void pngwriter::flood_fill_internal_blend(int xstart, int ystart, double opacity,  double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue)
+{
+   if( (
+	(this->dread(xstart,ystart,1) == start_red) &&
+	(this->dread(xstart,ystart,2) == start_green) &&
+	(this->dread(xstart,ystart,3) == start_blue)
+	)
+       &&
+       (
+	(this->dread(xstart,ystart,1) != fill_red) ||
+	(this->dread(xstart,ystart,2) != fill_green) ||
+	(this->dread(xstart,ystart,3) != fill_blue)
+	)
+       &&
+       (xstart >0)&&(xstart <= width_)&&(ystart >0)&&(ystart <= height_)
+       )
+     {
+	this->plot_blend(xstart, ystart, opacity, fill_red, fill_green, fill_blue);
+	flood_fill_internal_blend(  xstart+1,  ystart, opacity,   start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
+	flood_fill_internal_blend(  xstart-1,  ystart,opacity,   start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
+	flood_fill_internal_blend(  xstart,  ystart+1, opacity,  start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
+	flood_fill_internal_blend(  xstart,  ystart-1, opacity,  start_red,  start_green,  start_blue,  fill_red,  fill_green,  fill_blue);
+     }
+
+}
+
+//int version
+void pngwriter::boundary_fill_blend(int xstart, int ystart, double opacity, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue)
+{
+
+   this->boundary_fill_blend( xstart, ystart,
+			      opacity,
+			      ((double) boundary_red)/65535.0,
+			      ((double) boundary_green)/65535.0,
+			      ((double) boundary_blue)/65535.0,
+			      ((double) fill_red)/65535.0,
+			      ((double) fill_green)/65535.0,
+			      ((double) fill_blue)/65535.0
+			      );
+}
+
+void pngwriter::flood_fill_blend(int xstart, int ystart, double opacity, double fill_red, double fill_green, double fill_blue)
+{
+   flood_fill_internal_blend(  xstart,  ystart, opacity,  this->dread(xstart,ystart,1),this->dread(xstart,ystart,2),this->dread(xstart,ystart,3),   fill_red,  fill_green, fill_blue);
+}
+
+//int version
+void pngwriter::flood_fill_blend(int xstart, int ystart, double opacity, int fill_red, int fill_green, int fill_blue)
+{
+   this->flood_fill_blend( xstart,  ystart,
+			   opacity,
+			   ((double)  fill_red)/65535.0,
+			   ((double) fill_green)/65535.0,
+			   ((double)  fill_blue)/65535.0
+			   );
+}
+
+void pngwriter::polygon_blend( int * points, int number_of_points, double opacity,  double red, double green, double blue)
+{
+   if( (number_of_points<1)||(points ==NULL))
+     {
+	std::cerr << " PNGwriter::polygon_blend - ERROR **:  Number of points is zero or negative, or array is NULL." << std::endl;
+	return;
+     }
+
+   for(int k=0;k< number_of_points-1; k++)
+     {
+	this->line_blend(points[2*k],points[2*k+1],points[2*k+2],points[2*k+3], opacity,  red, green, blue);
+     }
+}
+
+//int version
+void pngwriter::polygon_blend( int * points, int number_of_points, double opacity, int red, int green, int blue)
+{
+   this->polygon_blend(points, number_of_points,
+		       opacity,
+		       ((double) red)/65535.0,
+		       ((double) green)/65535.0,
+		       ((double) blue)/65535.0
+		       );
+}
+
+void pngwriter::plotCMYK_blend(int x, int y, double opacity, double cyan, double magenta, double yellow, double black)
+{
+/*CMYK to RGB:
+ *  -----------
+ *  red   = 255 - minimum(255,((cyan/255)    * (255 - black) + black))
+ *  green = 255 - minimum(255,((magenta/255) * (255 - black) + black))
+ *  blue  = 255 - minimum(255,((yellow/255)  * (255 - black) + black))
+ * */
+
+   if(cyan<0.0)
+     {
+	cyan = 0.0;
+     }
+   if(magenta<0.0)
+     {
+	magenta = 0.0;
+     }
+   if(yellow<0.0)
+     {
+	yellow = 0.0;
+     }
+   if(black<0.0)
+     {
+	black = 0.0;
+     }
+
+   if(cyan>1.0)
+     {
+	cyan = 1.0;
+     }
+   if(magenta>1.0)
+     {
+	magenta = 1.0;
+     }
+   if(yellow>1.0)
+     {
+	yellow = 1.0;
+     }
+   if(black>1.0)
+     {
+	black = 1.0;
+     }
+
+   double  red, green, blue, minr, ming, minb, iblack;
+
+   iblack = 1.0 - black;
+
+   minr = 1.0;
+   ming = 1.0;
+   minb = 1.0;
+
+   if( (cyan*iblack + black)<1.0 )
+     {
+	minr = cyan*iblack + black;
+     }
+
+   if( (magenta*iblack + black)<1.0 )
+     {
+	ming = magenta*iblack + black;
+     }
+
+   if( (yellow*iblack + black)<1.0 )
+     {
+	minb = yellow*iblack + black;
+     }
+
+   red = 1.0 - minr;
+   green = 1.0 - ming;
+   blue = 1.0 - minb;
+
+   this->plot_blend(x,y,opacity, red, green, blue);
+
+}
+
+//int version
+void pngwriter::plotCMYK_blend(int x, int y, double opacity, int cyan, int magenta, int yellow, int black)
+{
+   this->plotCMYK_blend( x, y,
+			 opacity,
+			 ((double) cyan)/65535.0,
+			 ((double) magenta)/65535.0,
+			 ((double) yellow)/65535.0,
+			 ((double) black)/65535.0
+			 );
+}
+
+void pngwriter::laplacian(double k, double offset)
+{
+
+   // Create image storage.
+   pngwriter temp(width_,height_,0,"temp");
+
+   double red, green, blue;
+
+   for(int x = 1; x <= width_; x++)
+     {
+	for(int y = 1; y <= height_; y++)
+	  {
+	     red =
+	       8.0*this->dread(x,y,1) -
+	       ( this->dread(x+1, y-1, 1) +
+		 this->dread(x,   y-1, 1) +
+		 this->dread(x-1, y-1, 1) +
+		 this->dread(x-1, y,   1) +
+		 this->dread(x+1, y,   1) +
+		 this->dread(x+1, y+1, 1) +
+		 this->dread(x,   y+1, 1) +
+		 this->dread(x-1, y+1, 1) );
+
+	     green =
+	       8.0*this->dread(x,y,2) -
+	       ( this->dread(x+1, y-1, 2) +
+		 this->dread(x,   y-1, 2) +
+		 this->dread(x-1, y-1, 2) +
+		 this->dread(x-1, y,   2) +
+		 this->dread(x+1, y,   2) +
+		 this->dread(x+1, y+1, 2) +
+		 this->dread(x,   y+1, 2) +
+		 this->dread(x-1, y+1, 2));
+
+	     blue =
+	       8.0*this->dread(x,y,3) -
+	       ( this->dread(x+1, y-1, 3) +
+		 this->dread(x,   y-1, 3) +
+		 this->dread(x-1, y-1, 3) +
+		 this->dread(x-1, y,   3) +
+		 this->dread(x+1, y,   3) +
+		 this->dread(x+1, y+1, 3) +
+		 this->dread(x,   y+1, 3) +
+		 this->dread(x-1, y+1, 3));
+
+	     temp.plot(x,y,offset+k*red,offset+k*green,offset+k*blue);
+
+	  }
+     }
+
+   for(int xx = 1; xx <= width_; xx++)
+     {
+	for(int yy = 1; yy <= height_; yy++)
+	  {
+	     this->plot(xx,yy,  temp.read(xx,yy,1), temp.read(xx,yy,2), temp.read(xx,yy,3));
+	  }
+     }
+}
+
+
+
+// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun
+// ( , http://www.linuks.mine.nu/ )
+void pngwriter::drawtop(long x1,long y1,long x2,long y2,long x3, int red, int green, int blue)
+{
+   // This swaps x2 and x3
+   // if(x2>x3) x2^=x3^=x2^=x3;
+   if(x2>x3)
+   {
+   x2^=x3;
+   x3^=x2;
+   x2^=x3;
+   }
+
+   long posl = x1*256;
+   long posr = posl;
+
+   long cl=((x2-x1)*256)/(y2-y1);
+   long cr=((x3-x1)*256)/(y2-y1);
+
+   for(int y=y1; yline(posl/256, y, posr/256, y, red, green, blue);
+	posl+=cl;
+	posr+=cr;
+     }
+}
+
+// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun
+// ( , http://www.linuks.mine.nu/ )
+void pngwriter::drawbottom(long x1,long y1,long x2,long x3,long y3, int red, int green, int blue)
+{
+   //Swap x1 and x2
+   //if(x1>x2) x2^=x1^=x2^=x1;
+   if(x1>x2)
+   {
+   x2^=x1;
+   x1^=x2;
+   x2^=x1;
+    }
+
+   long posl=x1*256;
+   long posr=x2*256;
+
+   long cl=((x3-x1)*256)/(y3-y1);
+   long cr=((x3-x2)*256)/(y3-y1);
+
+   for(int y=y1; yline(posl/256, y, posr/256, y, red, green, blue);
+
+	posl+=cl;
+	posr+=cr;
+     }
+}
+
+// drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun
+// ( , http://www.linuks.mine.nu/ )
+void pngwriter::filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, int red, int green, int blue)
+{
+   if((x1==x2 && x2==x3) || (y1==y2 && y2==y3)) return;
+
+   if(y2drawtop(x1, y1, x2, y2, x3, red, green, blue);
+     }
+   else
+     {
+	if(y1==y3 || y1==y2)
+	  {
+	     this->drawbottom(x1, y1, x2, x3, y3, red, green, blue);
+	  }
+	else
+	  {
+	     int new_x = x1 + (int)((double)(y2-y1)*(double)(x3-x1)/(double)(y3-y1));
+	     this->drawtop(x1, y1, new_x, y2, x2, red, green, blue);
+	     this->drawbottom(x2, y2, new_x, x3, y3, red, green, blue);
+	  }
+     }
+
+}
+
+//Double (bug found by Dave Wilks. Was: (int) red*65535, should have been (int) (red*65535).
+void pngwriter::filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, double red, double green, double blue)
+{
+   this->filledtriangle(x1, y1, x2, y2, x3, y3, (int) (red*65535), (int) (green*65535),  (int) (blue*65535)); 
+}
+
+//Blend, double. (bug found by Dave Wilks. Was: (int) red*65535, should have been (int) (red*65535).
+void pngwriter::filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, double red, double green, double blue)
+{
+   this->filledtriangle_blend( x1, y1, x2, y2, x3, y3,  opacity,  (int) (red*65535), (int) (green*65535),  (int) (blue*65535)); 
+}
+
+//Blend, int
+void pngwriter::filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, int red, int green, int blue)
+{
+   if((x1==x2 && x2==x3) || (y1==y2 && y2==y3)) return;
+
+   /*if(y2drawtop_blend(x1, y1, x2, y2, x3, opacity, red, green, blue);
+     }
+   else
+     {
+	if(y1==y3 || y1==y2)
+	  {
+	     this->drawbottom_blend(x1, y1, x2, x3, y3, opacity, red, green, blue);
+	  }
+	else
+	  {
+	     int new_x = x1 + (int)((double)(y2-y1)*(double)(x3-x1)/(double)(y3-y1));
+	     this->drawtop_blend(x1, y1, new_x, y2, x2, opacity,  red, green, blue);
+	     this->drawbottom_blend(x2, y2, new_x, x3, y3, opacity, red, green, blue);
+	  }
+     }
+
+}
+
+//Blend, int
+void pngwriter::drawbottom_blend(long x1,long y1,long x2,long x3,long y3, double opacity, int red, int green, int blue)
+{
+   //Swap x1 and x2
+   if(x1>x2)
+   {
+   x2^=x1;
+   x1^=x2;
+   x2^=x1;
+   }
+
+   long posl=x1*256;
+   long posr=x2*256;
+
+   long cl=((x3-x1)*256)/(y3-y1);
+   long cr=((x3-x2)*256)/(y3-y1);
+
+   for(int y=y1; yline_blend(posl/256, y, posr/256, y, opacity, red, green, blue);
+
+	posl+=cl;
+	posr+=cr;
+     }
+
+}
+
+//Blend, int
+void pngwriter::drawtop_blend(long x1,long y1,long x2,long y2,long x3, double opacity, int red, int green, int blue)
+{
+   // This swaps x2 and x3
+   if(x2>x3)
+   {
+   x2^=x3;
+   x3^=x2;
+   x2^=x3;
+}
+
+   long posl = x1*256;
+   long posr = posl;
+
+   long cl=((x2-x1)*256)/(y2-y1);
+   long cr=((x3-x1)*256)/(y2-y1);
+
+   for(int y=y1; yline_blend(posl/256, y, posr/256, y, opacity, red, green, blue);
+	posl+=cl;
+	posr+=cr;
+     }
+
+}
+
+void pngwriter::triangle(int x1, int y1, int x2, int y2, int x3, int y3, int red, int green, int blue)
+{
+   this->line(x1, y1, x2, y2, red, green, blue);
+   this->line(x2, y2, x3, y3, red, green, blue);
+   this->line(x3, y3, x1, y1, red, green, blue);
+}
+
+void pngwriter::triangle(int x1, int y1, int x2, int y2, int x3, int y3, double red, double green, double blue)
+{
+   
+   this->line(x1, y1, x2, y2, ((int)65535*red), ((int)65535*green), ((int)65535*blue));
+   this->line(x2, y2, x3, y3, ((int)65535*red), ((int)65535*green), ((int)65535*blue));
+   this->line(x3, y3, x1, y1, ((int)65535*red), ((int)65535*green), ((int)65535*blue));
+   
+}
+
+
+
+
+
+void pngwriter::arrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue)
+{
+
+   this->line(x1, y1, x2, y2, red, green, blue);
+   //   double th = 3.141592653589793 + (head_angle)*3.141592653589793/180.0;  //degrees
+   double th = 3.141592653589793 + head_angle;
+   double costh = cos(th);
+   double sinth = sin(th);
+   double t1, t2, r;   
+   t1 = ((x2-x1)*costh - (y2-y1)*sinth);
+   t2 = ((x2-x1)*sinth + (y2-y1)*costh);
+   r = sqrt(t1*t1 + t2*t2);
+
+   double advancex  = size*t1/r;
+   double advancey  = size*t2/r;
+   this->line(x2, y2, int(x2 + advancex), int(y2 + advancey), red, green, blue);
+   t1 = (x2-x1)*costh + (y2-y1)*sinth;
+   t2 =   (y2-y1)*costh - (x2-x1)*sinth;
+ 
+   advancex  = size*t1/r;
+   advancey  = size*t2/r;
+   this->line(x2, y2, int(x2 + advancex), int(y2 + advancey), red, green, blue);
+}
+   
+void pngwriter::filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue)
+{
+   int p1x, p2x, p3x, p1y, p2y, p3y;
+   
+   this->line(x1, y1, x2, y2, red, green, blue);
+   double th = 3.141592653589793 + head_angle;
+   double costh = cos(th);
+   double sinth = sin(th);
+   double t11, t12, t21, t22, r1, r2;
+   t11 = ((x2-x1)*costh - (y2-y1)*sinth);
+   t21 = ((x2-x1)*sinth + (y2-y1)*costh);
+   t12 = (x2-x1)*costh + (y2-y1)*sinth;
+   t22 =   (y2-y1)*costh - (x2-x1)*sinth;
+
+   r1 = sqrt(t11*t11 + t21*t21);
+   r2 = sqrt(t12*t12 + t22*t22);
+   
+   double advancex1  = size*t11/r1;
+   double advancey1  = size*t21/r1;
+   double advancex2  = size*t12/r2;
+   double advancey2  = size*t22/r2;
+
+   p1x = x2;
+   p1y = y2;
+   
+   p2x = int(x2 + advancex1);
+   p2y = int(y2 + advancey1);
+
+   p3x = int(x2 + advancex2);
+   p3y = int(y2 + advancey2);
+
+   
+   this->filledtriangle( p1x,  p1y,  p2x,  p2y,  p3x,  p3y, red, green,  blue);
+   
+}
+
+void pngwriter::arrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue)
+{
+   this->arrow(  x1, y1, x2, y2, size,  head_angle,  (double (red))/65535.0,  (double (green))/65535.0,  (double (blue))/65535.0 );
+}
+
+void pngwriter::filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue)
+{
+   this->filledarrow(  x1, y1, x2, y2, size,  head_angle, (double (red))/65535.0,  (double (green))/65535.0, (double (blue))/65535.0 );
+}
+
+
+void pngwriter::cross( int x, int y, int xwidth, int yheight, int red, int green, int blue)
+{
+   this->line(int(x - xwidth/2.0), y, int(x + xwidth/2.0), y, red, green, blue);
+   this->line(x, int(y - yheight/2.0), x, int(y + yheight/2.0), red, green, blue);
+}
+
+void pngwriter::maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, int red, int green, int blue)
+{
+   this->line(int(x - xwidth/2.0), y, int(x + xwidth/2.0), y, red, green, blue);
+   this->line(x, int(y - yheight/2.0), x, int(y + yheight/2.0), red, green, blue);
+   // Bars on ends of vertical line
+   this->line(int(x - y_bar_width/2.0), int(y + yheight/2.0), int(x + y_bar_width/2.0), int(y + yheight/2.0), red, green, blue);
+   this->line(int(x - y_bar_width/2.0), int(y - yheight/2.0), int(x + y_bar_width/2.0), int(y - yheight/2.0), red, green, blue);
+   // Bars on ends of horizontal line.
+   this->line(int(x - xwidth/2.0), int(y - x_bar_height/2.0), int(x - xwidth/2.0), int(y + x_bar_height/2.0), red, green, blue);
+   this->line(int(x + xwidth/2.0), int(y - x_bar_height/2.0), int(x + xwidth/2.0), int(y + x_bar_height/2.0), red, green, blue);
+}
+
+void pngwriter::cross( int x, int y, int xwidth, int yheight, double red, double green, double blue)
+{
+   this->cross( x, y, xwidth, yheight, int(65535*red), int(65535*green), int(65535*blue)); 
+}
+
+void pngwriter::maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, double red, double green, double blue)
+{
+   this->maltesecross( x, y, xwidth, yheight, x_bar_height, y_bar_width, int(65535*red), int(65535*green), int(65535*blue)); 
+}
+
+
+void pngwriter::filleddiamond( int x, int y, int width, int height, int red, int green, int blue)
+{
+   this->filledtriangle( int(x - width/2.0), y, x, y, x, int(y + height/2.0), red, green, blue);
+   this->filledtriangle( int(x + width/2.0), y, x, y, x, int(y + height/2.0), red, green, blue);
+   this->filledtriangle( int(x - width/2.0), y, x, y, x, int(y - height/2.0), red, green, blue);
+   this->filledtriangle( int(x + width/2.0), y, x, y, x, int(y - height/2.0), red, green, blue);
+}
+
+void pngwriter::diamond( int x, int y, int width, int height, int red, int green, int blue)
+{
+   this->line( int(x - width/2.0), y, x, int(y + height/2.0), red, green, blue);
+   this->line( int(x + width/2.0), y, x, int(y + height/2.0), red, green, blue);
+   this->line( int(x - width/2.0), y, x, int(y - height/2.0), red, green, blue);
+   this->line( int(x + width/2.0), y, x, int(y - height/2.0), red, green, blue);
+}
+
+
+void pngwriter::filleddiamond( int x, int y, int width, int height, double red, double green, double blue)
+{
+   this->filleddiamond(  x, y,  width,  height, int(red*65535), int(green*65535), int(blue*65535) ); 
+}
+
+void pngwriter::diamond( int x, int y, int width, int height, double red, double green, double blue)
+{
+   this->diamond(  x,  y,  width,  height, int(red*65535), int(green*65535), int(blue*65535) ); 
+}
+
diff --git a/libsrc/hyperionpng/pngwriter.h b/libsrc/hyperionpng/pngwriter.h
new file mode 100644
index 00000000..ab01b1ae
--- /dev/null
+++ b/libsrc/hyperionpng/pngwriter.h
@@ -0,0 +1,745 @@
+//**********  pngwriter.h   **********************************************
+//  Author:                    Paul Blackburn
+//
+//  Email:                     individual61@users.sourceforge.net
+//
+//  Version:                   0.5.4   (19 / II / 2009)
+//
+//  Description:               Library that allows plotting a 48 bit
+//                             PNG image pixel by pixel, which can 
+//                             then be opened with a graphics program.
+//  
+//  License:                   GNU General Public License
+//                             Copyright 2002, 2003, 2004, 2005, 2006, 2007,
+//                             2008, 2009 Paul Blackburn
+//                             
+//  Website: Main:             http://pngwriter.sourceforge.net/
+//           Sourceforge.net:  http://sourceforge.net/projects/pngwriter/
+//           Freshmeat.net:    http://freshmeat.net/projects/pngwriter/
+//           
+//  Documentation:             This header file is commented, but for a
+//                             quick reference document, and support,
+//                             take a look at the website.
+//
+//*************************************************************************
+
+
+/*
+ *     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.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * */
+
+#ifndef PNGWRITER_H
+#define PNGWRITER_H 1
+
+#define PNGWRITER_VERSION 0.54
+
+#include 
+
+// REMEMBER TO ADD -DNO_FREETYPE TO YOUR COMPILATION FLAGS IF PNGwriter WAS
+// COMPILED WITHOUT FREETYPE SUPPORT!!!
+// 
+// RECUERDA AGREGAR -DNO_FREETYPE A TUS OPCIONES DE COMPILACION SI PNGwriter 
+// FUE COMPILADO SIN SOPORTE PARA FREETYPE!!!
+// 
+#ifndef NO_FREETYPE
+#include 
+#include FT_FREETYPE_H
+#endif
+
+
+
+#ifdef OLD_CPP // For compatibility with older compilers.
+#include 
+#include 
+#include 
+#include 
+using namespace std;
+#endif // from ifdef OLD_CPP
+
+#ifndef OLD_CPP // Default situation.
+#include 
+#include 
+#include 
+#include 
+#endif // from ifndef OLD_CPP
+
+
+//png.h must be included before FreeType headers.
+#include 
+#include 
+#include 
+
+
+
+
+#define PNG_BYTES_TO_CHECK (4)
+#define PNGWRITER_DEFAULT_COMPRESSION (6)
+
+class pngwriter 
+{
+ private:
+   
+   char * filename_;   
+   char * textauthor_;   
+   char * textdescription_;   
+   char * texttitle_;   
+   char * textsoftware_;   
+
+
+   
+   int height_;
+   int width_;
+   int  backgroundcolour_;
+   int bit_depth_;
+   int rowbytes_;
+   int colortype_;
+   int compressionlevel_;
+   bool transformation_; // Required by Mikkel's patch
+   
+   unsigned char * * graph_;
+   double filegamma_;
+   double screengamma_;
+   void circle_aux(int xcentre, int ycentre, int x, int y, int red, int green, int blue);
+   void circle_aux_blend(int xcentre, int ycentre, int x, int y, double opacity, int red, int green, int blue);
+   int check_if_png(char *file_name, FILE **fp);
+   int read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr);
+   int read_png_image(FILE *fp, png_structp png_ptr, png_infop info_ptr,
+ 		       png_bytepp *image, png_uint_32 *width, png_uint_32 *height);
+   void flood_fill_internal( int xstart, int ystart,  double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue);
+   void flood_fill_internal_blend( int xstart, int ystart, double opacity,  double start_red, double start_green, double start_blue, double fill_red, double fill_green, double fill_blue);
+
+#ifndef NO_FREETYPE
+   void my_draw_bitmap( FT_Bitmap * bitmap, int x, int y, double red, double green, double blue);
+   void my_draw_bitmap_blend( FT_Bitmap * bitmap, int x, int y,double opacity,  double red, double green, double blue);
+#endif
+   
+   /* The algorithms HSVtoRGB and RGBtoHSV were found at http://www.cs.rit.edu/~ncs/
+    * which is a page that belongs to Nan C. Schaller, though
+    * these algorithms appear to be the work of Eugene Vishnevsky. 
+    * */
+   void HSVtoRGB( double *r, double *g, double *b, double h, double s, double v ); 
+   void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v );
+
+   /* drwatop(), drawbottom() and filledtriangle() were contributed by Gurkan Sengun
+    * ( , http://www.linuks.mine.nu/ )
+    * */
+   void drawtop(long x1,long y1,long x2,long y2,long x3, int red, int green, int blue);
+   void drawbottom(long x1,long y1,long x2,long x3,long y3, int red, int green, int blue);
+   void drawbottom_blend(long x1,long y1,long x2,long x3,long y3, double opacity, int red, int green, int blue);
+   void drawtop_blend(long x1,long y1,long x2,long y2,long x3, double opacity, int red, int green, int blue);
+   
+ public:
+
+   /* General Notes
+    * It is important to remember that all functions that accept an argument of type "const char *" will also
+    * accept "char *", this is done so you can have a changing filename (to make many PNG images in series 
+    * with a different name, for example), and to allow you to use string type objects which can be easily 
+    * turned into const char * (if theString is an object of type string, then it can be used as a const char *
+    * by saying theString.c_str()).
+    * It is also important to remember that whenever a function has a colour coeffiecient as its argument, 
+    * that argument can be either an int from 0 to 65535 or a double from 0.0 to 1.0. 
+    * It is important to make sure that you are calling the function with the type that you want.
+    * Remember that 1 is an int, while 1.0 is a double, and will thus determine what version of the function 
+    * will be used. Similarly, do not make the mistake of calling for example plot(x, y, 0.0, 0.0, 65535),
+    * because
+    * there is no plot(int, int, double, double, int).
+    * Also, please note that plot() and read() (and the functions that use them internally) 
+    * are protected against entering, for example, a colour coefficient that is over 65535
+    * or over 1.0. Similarly, they are protected against negative coefficients. read() will return 0
+    * when called outside the image range. This is actually useful as zero-padding should you need it.
+    * */
+
+   /* Compilation
+    * A typical compilation would look like this:
+    * 
+    * g++ my_program.cc -o my_program freetype-config --cflags \
+    *          -I/usr/local/include  -L/usr/local/lib -lpng -lpngwriter -lz -lfreetype
+    * 
+    * If you did not compile PNGwriter with FreeType support, then remove the
+    * FreeType-related flags and add -DNO_FREETYPE above.
+    * */
+   
+   /* Constructor
+    * The constructor requires the width and the height of the image, the background colour for the
+    * image and the filename of the file (a pointer or simple "myfile.png"). The background colour
+    * can only be initialized to a shade of grey (once the object has been created you can do whatever 
+    * you want, though), because generally one wants either a white (65535 or 1.0) or a black (0 or 0.0)
+    * background to start with.
+    * The default constructor creates a PNGwriter instance that is 250x250, white background,
+    * and filename "out.png".
+    * Tip: The filename can be given as easily as:
+    * pngwriter mypng(300, 300, 0.0, "myfile.png");    
+    * Tip: If you are going to create a PNGwriter instance for reading in a file that already exists, 
+    * then width and height can be 1 pixel, and the size will be automatically adjusted once you use
+    * readfromfile().
+    * */
+   pngwriter();   
+   pngwriter(const pngwriter &rhs);
+   pngwriter(int width, int height, int backgroundcolour, char * filename);   
+   pngwriter(int width, int height, double backgroundcolour, char * filename);    
+   pngwriter(int width, int height, int backgroundcolour, const char * filename);   
+   pngwriter(int width, int height, double backgroundcolour, const char * filename);    
+
+   /* Destructor
+    * */
+   ~pngwriter();  
+
+   /* Assignment Operator
+    * */
+   pngwriter & operator = (const pngwriter & rhs);
+      
+   /*  Plot
+    * With this function a pixel at coordinates (x, y) can be set to the desired colour. 
+    * The pixels are numbered starting from (1, 1) and go to (width, height). 
+    * As with most functions in PNGwriter, it has been overloaded to accept either int arguments 
+    * for the colour coefficients, or those of type double. If they are of type int, 
+    * they go from 0 to 65535. If they are of type double, they go from 0.0 to 1.0.
+    * Tip: To plot using red, then specify plot(x, y, 1.0, 0.0, 0.0). To make pink, 
+    * just add a constant value to all three coefficients, like this:
+    * plot(x, y, 1.0, 0.4, 0.4). 
+    * Tip: If nothing is being plotted to your PNG file, make sure that you remember
+    * to close() the instance before your program is finished, and that the x and y position
+    * is actually within the bounds of your image. If either is not, then PNGwriter will 
+    * not complain-- it is up to you to check for this!
+    * Tip: If you try to plot with a colour coefficient out of range, a maximum or minimum
+    * coefficient will be assumed, according to the given coefficient. For example, attempting
+    * to plot plot(x, y, 1.0,-0.2,3.7) will set the green coefficient to 0 and the red coefficient
+    * to 1.0.
+    * */
+   void  plot(int x, int y, int red, int green, int blue); 
+   void  plot(int x, int y, double red, double green, double blue); 
+                                                          
+   /* Plot HSV
+    * With this function a pixel at coordinates (x, y) can be set to the desired colour, 
+    * but with the colour coefficients given in the Hue, Saturation, Value colourspace. 
+    * This has the advantage that one can determine the colour that will be plotted with 
+    * only one parameter, the Hue. The colour coefficients must go from 0 to 65535 and
+    * be of type int, or be of type double and go from 0.0 to 1.0.
+    * */
+   void plotHSV(int x, int y, double hue, double saturation, double value);
+   void plotHSV(int x, int y, int hue, int saturation, int value); 
+
+   /* Read
+    * With this function we find out what colour the pixel (x, y) is. If "colour" is 1,
+    * it will return the red coefficient, if it is set to 2, the green one, and if 
+    * it set to 3, the blue colour coefficient will be returned,
+    * and this returned value will be of type int and be between 0 and 65535.
+    * Note that if you call read() on a pixel outside the image range, the value returned
+    * will be 0.
+    * */
+   int read(int x, int y, int colour);  
+
+   /* Read, Average
+    * Same as the above, only that the average of the three colour coefficients is returned.
+    */
+   int read(int x, int y);              
+   
+  /* dRead
+   * With this function we find out what colour the pixel (x, y) is. If "colour" is 1,
+   * it will return the red coefficient, if it is set to 2, the green one, and if 
+   * it set to 3, the blue colour coefficient will be returned,
+   * and this returned value will be of type double and be between 0.0 and 1.0.
+   * Note that if you call dread() outside the image range, the value returned will be 0.0
+   * */
+   double dread(int x, int y, int colour);   
+   
+   /* dRead, Average
+    * Same as the above, only that the average of the three colour coefficients is returned.
+    */
+   double dread(int x, int y);             
+   
+   /* Read HSV
+    * With this function we find out what colour the pixel (x, y) is, but in the Hue, 
+    * Saturation, Value colourspace. If "colour" is 1,
+    * it will return the Hue coefficient, if it is set to 2, the Saturation one, and if 
+    * it set to 3, the Value colour coefficient will be returned, and this returned
+    * value will be of type int and be between 0 and 65535. Important: If you attempt
+    * to read the Hue of a pixel that is a shade of grey, the value returned will be
+    * nonsensical or even NaN. This is just the way the RGB -> HSV algorithm works: 
+    * the Hue of grey is not defined. You might want to check whether the pixel
+    * you are reading is grey before attempting a readHSV().
+    * Tip: This is especially useful for categorizing sections of the image according 
+    * to their colour. 
+    * */
+   int readHSV(int x, int y, int colour);  
+    
+  /* dRead HSV
+   * With this function we find out what colour the pixel (x, y) is, but in the Hue, 
+   * Saturation, Value colourspace. If "colour" is 1,
+   * it will return the Hue coefficient, if it is set to 2, the Saturation one, and if 
+   * it set to 3, the Value colour coefficient will be returned,
+   * and this returned value will be of type double and be between 0.0 and 1.0.
+   * */
+   double dreadHSV(int x, int y, int colour);    
+   
+   /* Clear
+    * The whole image is set to black.
+    * */ 
+   void clear(void);    
+   
+   /* Close
+    * Close the instance of the class, and write the image to disk.
+    * Tip: If you do not call this function before your program ends, no image
+    * will be written to disk.
+    * */
+   void close(void); 
+
+   /* Rename
+    * To rename the file once an instance of pngwriter has been created.
+    * Useful for assigning names to files based upon their content.
+    * Tip: This is as easy as calling pngwriter_rename("newname.png")
+    * If the argument is a long unsigned int, for example 77, the filename will be changed to
+    * 0000000077.png
+    * Tip: Use this to create sequences of images for movie generation.
+    * */
+   void pngwriter_rename(char * newname);               
+   void pngwriter_rename(const char * newname);            
+   void pngwriter_rename(long unsigned int index);            
+
+   /* Figures
+    * These functions draw basic shapes. Available in both int and double versions.
+    * The line functions use the fast Bresenham algorithm. Despite the name, 
+    * the square functions draw rectangles. The circle functions use a fast 
+    * integer math algorithm. The filled circle functions make use of sqrt().
+    * */
+   void line(int xfrom, int yfrom, int xto, int yto, int red, int green,int  blue);
+   void line(int xfrom, int yfrom, int xto, int yto, double red, double green,double  blue);
+  
+   void triangle(int x1, int y1, int x2, int y2, int x3, int y3, int red, int green, int blue);
+   void triangle(int x1, int y1, int x2, int y2, int x3, int y3, double red, double green, double blue);
+   
+   void square(int xfrom, int yfrom, int xto, int yto, int red, int green,int  blue);
+   void square(int xfrom, int yfrom, int xto, int yto, double red, double green,double  blue);
+   
+   void filledsquare(int xfrom, int yfrom, int xto, int yto, int red, int green,int  blue);
+   void filledsquare(int xfrom, int yfrom, int xto, int yto, double red, double green,double  blue);
+
+   void circle(int xcentre, int ycentre, int radius, int red, int green, int blue);
+   void circle(int xcentre, int ycentre, int radius, double red, double green, double blue);
+   
+   void filledcircle(int xcentre, int ycentre, int radius, int red, int green, int blue);
+   void filledcircle(int xcentre, int ycentre, int radius, double red, double green, double blue);
+
+   
+   /* Read From File
+    * Open the existing PNG image, and copy it into this instance of the class. It is important to mention 
+    * that PNG variants are supported. Very generally speaking, most PNG files can now be read (as of version 0.5.4), 
+    * but if they have an alpha channel it will be completely stripped. If the PNG file uses GIF-style transparency 
+    * (where one colour is chosen to be transparent), PNGwriter will not read the image properly, but will not 
+    * complain. Also, if any ancillary chunks are included in the PNG file (chroma, filter, etc.), it will render 
+    * with a slightly different tonality. For the vast majority of PNGs, this should not be an issue. Note: 
+    * If you read an 8-bit PNG, the internal representation of that instance of PNGwriter will be 8-bit (PNG 
+    * files of less than 8 bits will be upscaled to 8 bits). To convert it to 16-bit, just loop over all pixels, 
+    * reading them into a new instance of PNGwriter. New instances of PNGwriter are 16-bit by default.
+    * */
+
+   void readfromfile(char * name);  
+   void readfromfile(const char * name); 
+
+   /* Get Height
+    * When you open a PNG with readfromfile() you can find out its height with this function.
+    * */
+   int getheight(void);
+   
+   /* Get Width
+    * When you open a PNG with readfromfile() you can find out its width with this function.
+    * */
+   int getwidth(void);
+
+   /* Set Compression Level
+    * Set the compression level that will be used for the image. -1 is to use the  default,
+    * 0 is none, 9 is best compression. 
+    * Remember that this will affect how long it will take to close() the image. A value of 2 or 3
+    * is good enough for regular use, but for storage or transmission you might want to take the time
+    * to set it at 9.
+    * */
+    void setcompressionlevel(int level);
+
+   /* Get Bit Depth
+    * When you open a PNG with readfromfile() you can find out its bit depth with this function.
+    * Mostly for troubleshooting uses.
+    * */
+   int getbitdepth(void);
+   
+   /* Get Colour Type
+    * When you open a PNG with readfromfile() you can find out its colour type (libpng categorizes 
+    * different styles of image data with this number).
+    * Mostly for troubleshooting uses.
+    * */
+   int getcolortype(void);
+   
+   /* Set Gamma Coeff
+    * Set the image's gamma (file gamma) coefficient. This is experimental, but use it if your image's colours seem too bright
+    * or too dark. The default value of 0.5 should be fine. The standard disclaimer about Mac and PC gamma
+    * settings applies.
+    * */
+   void setgamma(double gamma);
+
+   
+   /* Get Gamma Coeff
+    * Get the image's gamma coefficient. This is experimental.
+    * */
+   double getgamma(void);
+
+   /* Bezier Curve
+    * (After Frenchman Pierre BŽzier from Regie Renault)
+    * A collection of formulae for describing curved lines 
+    * and surfaces, first used in 1972 to model automobile surfaces. 
+    *             (from the The Free On-line Dictionary of Computing)
+    * See http://www.moshplant.com/direct-or/bezier/ for one of many
+    * available descriptions of bezier curves.
+    * There are four points used to define the curve: the two endpoints
+    * of the curve are called the anchor points, while the other points,
+    * which define the actual curvature, are called handles or control points.
+    * Moving the handles lets you modify the shape of the curve. 
+    * */ 
+     
+   void bezier(  int startPtX, int startPtY,                                                                            
+              int startControlX, int startControlY,                                                                                     
+              int endPtX, int endPtY,                                                                                                   
+              int endControlX, int endControlY,                                                                                         
+              double red, double green, double blue);
+
+   void bezier(  int startPtX, int startPtY,                                                                            
+              int startControlX, int startControlY,                                                                                     
+              int endPtX, int endPtY,                                                                                                   
+              int endControlX, int endControlY,                                                                                         
+              int red, int green, int blue);
+
+   /* Set Text
+    * Sets the text information in the PNG header. If it is not called, the default is used.
+    */
+   void settext(char * title, char * author, char * description, char * software);
+   void settext(const char * title, const char * author, const char * description, const char * software);
+
+
+   /* Version Number
+    * Returns the PNGwriter version number.
+    */
+  static double version(void);  
+
+   /* Write PNG
+    * Writes the PNG image to disk. You can still change the PNGwriter instance after this.
+    * Tip: This is exactly the same as close(), but easier to remember.
+    * Tip: To make a sequence of images using only one instance of PNGwriter, alter the image, change its name,
+    * write_png(), then alter the image, change its name, write_png(), etc.
+    */
+   void write_png(void);
+
+   /* Plot Text
+    * Uses the Freetype2 library to set text in the image. face_path is the file path to a 
+    * TrueType font file (.ttf) (FreeType2 can also handle other types). fontsize specifices the approximate
+    * height of the rendered font in pixels. x_start and y_start specify the placement of the
+    * lower, left corner of the text string. angle is the text angle in radians. text is the text to be rendered.
+    * The colour coordinates can be doubles from 0.0 to 1.0 or ints from 0 to 65535.
+    * Tip: PNGwriter installs a few fonts in /usr/local/share/pngwriter/fonts to get you started.
+    * Tip: Remember to add -DNO_FREETYPE to your compilation flags if PNGwriter was compiled without FreeType support.
+    * */
+   void plot_text(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue);
+   void plot_text(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue);
+
+   
+   /* Plot UTF-8 Text 
+    * Same as the above, but the text to be plotted is encoded in UTF-8. Why would you want this? To be able to plot
+    * all characters available in a large TrueType font, for example: for rendering Japenese, Chinese and other 
+    * languages not restricted to the standard 128 character ASCII space.
+    * Tip: The quickest way to get a string into UTF-8 is to write it in an adequate text editor, and save it as a file
+    * in UTF-8 encoding, which can then be read in in binary mode.
+    * */
+   void plot_text_utf8(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double red, double green, double blue);
+   void plot_text_utf8(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, int red, int green, int blue);
+
+
+   /* Bilinear Interpolation of Image
+    * Given a floating point coordinate (x from 0.0 to width, y from 0.0 to height),
+    * this function will return the interpolated colour intensity specified by 
+    * colour (where red = 1, green = 2, blue = 3).
+    * bilinear_interpolate_read() returns an int from 0 to 65535, and 
+    * bilinear_interpolate_dread() returns a double from 0.0 to 1.0.
+    * Tip: Especially useful for enlarging an image.
+    * */
+   int bilinear_interpolation_read(double x, double y, int colour);
+   double bilinear_interpolation_dread(double x, double y, int colour);
+   
+   /* Plot Blend
+    * Plots the colour given by red, green blue, but blended with the existing pixel
+    * value at that position. opacity is a double that goes from 0.0 to 1.0. 
+    * 0.0 will not change the pixel at all, and 1.0 will plot the given colour. 
+    * Anything in between will be a blend of both pixel levels. Please note: This is neither 
+	* alpha channel nor PNG transparency chunk support. This merely blends the plotted pixels.
+    * */
+   
+   void plot_blend(int x, int y, double opacity, int red, int green, int blue);
+   void plot_blend(int x, int y, double opacity, double red, double green, double blue);
+
+   
+   /* Invert
+    * Inverts the image in RGB colourspace.
+    * */
+   void invert(void);
+
+   /* Resize Image
+    * Resizes the PNGwriter instance. Note: All image data is set to black (this is 
+    * a resizing, not a scaling, of the image).
+    * */
+   void resize(int width, int height);
+
+   /* Boundary Fill
+    * All pixels adjacent to the start pixel will be filled with the fill colour, until the boundary colour is encountered. 
+    * For example, calling boundary_fill() with the boundary colour set to red, on a pixel somewhere inside a red circle,
+    * will fill the entire circle with the desired fill colour. If, on the other hand, the circle is not the boundary colour,
+    * the rest of the image will be filled. 
+    * The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535.
+    * */
+   void boundary_fill(int xstart, int ystart, double boundary_red,double boundary_green,double boundary_blue,double fill_red, double fill_green, double fill_blue) ;
+   void boundary_fill(int xstart, int ystart, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue) ;
+
+   /* Flood Fill
+    * All pixels adjacent to the start pixel will be filled with the fill colour, if they are the same colour as the
+    * start pixel. For example, calling flood_fill() somewhere in the interior of a solid blue rectangle will colour
+    * the entire rectangle the fill colour. The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535.
+    * */
+   void flood_fill(int xstart, int ystart, double fill_red, double fill_green, double fill_blue) ;
+   void flood_fill(int xstart, int ystart, int fill_red, int fill_green, int fill_blue) ;
+
+   /* Polygon
+    * This function takes an array of integer values containing the coordinates of the vertexes of a polygon. 
+    * Note that if you want a closed polygon, you must repeat the first point's coordinates for the last point.
+    * It also requires the number of points contained in the array. For example, if you wish to plot a triangle,
+    * the array will contain 6 elements, and the number of points is 3. Be very careful about this; if you specify the wrong number
+    * of points, your program will either segfault or produce points at nonsensical coordinates.
+    * The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535.
+    * */
+   void polygon(int * points, int number_of_points, double red, double green, double blue);
+   void polygon(int * points, int number_of_points, int red, int green, int blue);
+     
+   /* Plot CMYK
+    * Plot a point in the Cyan, Magenta, Yellow, Black colourspace. Please note that this colourspace is 
+    * lossy, i.e. it cannot reproduce all colours on screen that RGB can. The difference, however, is 
+    * barely noticeable. The algorithm used is a standard one. The colour components are either
+    * doubles from 0.0 to 1.0 or ints from 0 to 65535.
+    * */
+   void plotCMYK(int x, int y, double cyan, double magenta, double yellow, double black);
+   void plotCMYK(int x, int y, int cyan, int magenta, int yellow, int black);
+   
+   /* Read CMYK, Double version
+    * Get a pixel in the Cyan, Magenta, Yellow, Black colourspace. if 'colour' is 1, the Cyan component will be returned
+    * as a double from 0.0 to 1.0. If 'colour is 2, the Magenta colour component will be returned, and so on, up to 4.
+    * */
+   double dreadCMYK(int x, int y, int colour);
+
+   /* Read CMYK
+    * Same as the above, but the colour components returned are an int from 0 to 65535.
+    * */
+   int readCMYK(int x, int y, int colour);
+
+   /* Scale Proportional
+    * Scale the image using bilinear interpolation. If k is greater than 1.0, the image will be enlarged.
+    * If k is less than 1.0, the image will be shrunk. Negative or null values of k are not allowed.
+    * The image will be resized and the previous content will be replaced by the scaled image.
+    * Tip: use getheight() and getwidth() to find out the new width and height of the scaled image.
+    * Note: After scaling, all images will have a bit depth of 16, even if the original image had
+    * a bit depth of 8.
+    * */
+   void scale_k(double k);
+   
+   /* Scale Non-Proportional
+    * Scale the image using bilinear interpolation, with different horizontal and vertical scale factors.
+    * */
+   void scale_kxky(double kx, double ky);
+   
+   /* Scale To Target Width and Height
+    * Scale the image in such a way as to meet the target width and height. 
+    * Tip: if you want to keep the image proportional, scale_k() might be more appropriate.
+    * */
+   void scale_wh(int finalwidth, int finalheight);
+
+
+   /* Blended Functions
+    * All these functions are identical to their non-blended types. They take an extra argument, opacity, which is
+    * a double from 0.0 to 1.0 and represents how much of the original pixel value is retained when plotting the 
+    * new pixel. In other words, if opacity is 0.7, then after plotting, the new pixel will be 30% of the
+    * original colour the pixel was, and 70% of the new colour, whatever that may be. As usual, each function
+    * is available in int or double versions.  Please note: This is neither alpha channel nor PNG transparency chunk support. This merely blends the plotted pixels.
+    * */
+  
+   // Start Blended Functions
+  
+   void plotHSV_blend(int x, int y, double opacity, double hue, double saturation, double value);
+   void plotHSV_blend(int x, int y, double opacity, int hue, int saturation, int value);
+
+   void line_blend(int xfrom, int yfrom, int xto, int yto,  double opacity, int red, int green,int  blue);
+   void line_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double  blue);
+  
+   void square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int  blue);
+   void square_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double  blue);
+   
+   void filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, int red, int green,int  blue);
+   void filledsquare_blend(int xfrom, int yfrom, int xto, int yto, double opacity, double red, double green,double  blue);
+
+   void circle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue);
+   void circle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue);
+   
+   void filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, int red, int green, int blue);
+   void filledcircle_blend(int xcentre, int ycentre, int radius, double opacity, double red, double green, double blue);
+
+   void bezier_blend(  int startPtX, int startPtY,                                                                            
+		       int startControlX, int startControlY,                                                                                     
+		       int endPtX, int endPtY,                                                                                                   
+		       int endControlX, int endControlY,
+		       double opacity,
+		       double red, double green, double blue);
+   
+   void bezier_blend(  int startPtX, int startPtY,                                                                            
+		       int startControlX, int startControlY,                                                                                     
+		       int endPtX, int endPtY,                                                                                                   
+		       int endControlX, int endControlY,
+		       double opacity,
+		       int red, int green, int blue);
+   
+   void plot_text_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue);
+   void plot_text_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue);
+
+   void plot_text_utf8_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, double red, double green, double blue);
+   void plot_text_utf8_blend(char * face_path, int fontsize, int x_start, int y_start, double angle, char * text, double opacity, int red, int green, int blue);
+
+   void boundary_fill_blend(int xstart, int ystart, double opacity, double boundary_red,double boundary_green,double boundary_blue,double fill_red, double fill_green, double fill_blue) ;
+   void boundary_fill_blend(int xstart, int ystart, double opacity, int boundary_red,int boundary_green,int boundary_blue,int fill_red, int fill_green, int fill_blue) ;
+
+   void flood_fill_blend(int xstart, int ystart, double opacity, double fill_red, double fill_green, double fill_blue) ;
+   void flood_fill_blend(int xstart, int ystart, double opacity, int fill_red, int fill_green, int fill_blue) ;
+
+   void polygon_blend(int * points, int number_of_points, double opacity, double red, double green, double blue);
+   void polygon_blend(int * points, int number_of_points, double opacity,  int red, int green, int blue);
+     
+   void plotCMYK_blend(int x, int y, double opacity, double cyan, double magenta, double yellow, double black);
+   void plotCMYK_blend(int x, int y, double opacity, int cyan, int magenta, int yellow, int black);
+
+   // End of Blended Functions
+  
+   /* Laplacian
+    * This function applies a discrete laplacian to the image, multiplied by a constant factor. 
+    * The kernel used in this case is:
+    *                1.0  1.0  1.0
+    *                1.0 -8.0  1.0
+    *                1.0  1.0  1.0
+    * Basically, this works as an edge detector. The current pixel is assigned the sum of all neighbouring
+    * pixels, multiplied by the corresponding kernel element. For example, imagine a pixel and its 8 neighbours:
+    *                1.0    1.0    0.0  0.0
+    *                1.0  ->1.0<-  0.0  0.0
+    *                1.0    1.0    0.0  0.0
+    * This represents a border between white and black, black is on the right. Applying the laplacian to 
+    * the pixel specified above pixel gives:
+    *                1.0*1.0  + 1.0*1.0  + 0.0*1.0 +
+    *                1.0*1.0  + 1.0*-8.0 + 0.0*1.0 +
+    *                1.0*1.0  + 1.0*1.0  + 0.0*1.0   = -3.0
+    * Applying this to the pixel to the right of the pixel considered previously, we get a sum of 3.0.
+    * That is, after passing over an edge, we get a high value for the pixel adjacent to the edge. Since
+    * PNGwriter limits the colour components if they are off-scale, and the result of the laplacian
+    * may be negative, a scale factor and an offset value are included. This might be useful for
+    * keeping things within range or for bringing out more detail in the edge detection. The 
+    * final pixel value will be given by:
+    *   final value =  laplacian(original pixel)*k + offset
+    * Tip: Try a value of 1.0 for k to start with, and then experiment with other values.
+    * */
+    void laplacian(double k, double offset);
+
+   /* Filled Triangle
+    * Draws the triangle specified by the three pairs of points in the colour specified
+    * by the colour coefficients. The colour components are either doubles from 0.0 to 
+    * 1.0 or ints from 0 to 65535.
+    * */
+   void filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, int red, int green, int blue);
+   void filledtriangle(int x1,int y1,int x2,int y2,int x3,int y3, double red, double green, double blue);
+
+   /* Filled Triangle, Blended
+    * Draws the triangle specified by the three pairs of points in the colour specified
+    * by the colour coefficients, and blended with the background. See the description for Blended Functions.
+    * The colour components are either doubles from 0.0 to 1.0 or ints from 0 to 65535.
+    * */
+   void filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, int red, int green, int blue);
+   void filledtriangle_blend(int x1,int y1,int x2,int y2,int x3,int y3, double opacity, double red, double green, double blue);
+
+   /* Arrow,  Filled Arrow
+    * Plots an arrow from (x1, y1) to (x2, y2) with the arrowhead at the second point, given the size in pixels
+    * and the angle in radians of the arrowhead. The plotted arrow consists of one main line, and two smaller 
+    * lines originating from the second point. Filled Arrow plots the same, but the arrowhead is a solid triangle.
+    * Tip: An angle of 10 to 30 degrees looks OK.
+    * */
+   
+   void arrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue);
+   void arrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue);
+   
+   void filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, double red, double green, double blue);
+   void filledarrow( int x1,int y1,int x2,int y2,int size, double head_angle, int red, int green, int blue);
+
+   /* Cross, Maltese Cross
+    * Plots a simple cross at x, y, with the specified height and width, and in the specified colour.
+    * Maltese cross plots a cross, as before, but adds bars at the end of each arm of the cross.
+    * The size of these bars is specified with x_bar_height and y_bar_width.
+    * The cross will look something like this:
+    * 
+    *    -----  <-- ( y_bar_width)
+    *      |
+    *      |
+    *  |-------|  <-- ( x_bar_height )
+    *      |
+    *      |
+    *    -----
+    * */
+   
+   void cross( int x, int y, int xwidth, int yheight, double red, double green, double blue);
+   void cross( int x, int y, int xwidth, int yheight, int red, int green, int blue);
+
+   void maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, double red, double green, double blue);
+   void maltesecross( int x, int y, int xwidth, int yheight, int x_bar_height, int y_bar_width, int red, int green, int blue);
+
+   /* Diamond and filled diamond
+    * Plots a diamond shape, given the x, y position, the width and height, and the colour.
+    * Filled diamond plots a filled diamond.
+    * */
+   
+   void filleddiamond( int x, int y, int width, int height, int red, int green, int blue);
+   void diamond(int x, int y, int width, int height, int red, int green, int blue);
+   
+   void filleddiamond( int x, int y, int width, int height, double red, double green, double blue);
+   void diamond(int x, int y, int width, int height, double red, double green, double blue);
+
+   /* Get Text Width, Get Text Width UTF8
+    * Returns the approximate width, in pixels, of the specified *unrotated* text. It is calculated by adding 
+    * each letter's width and kerning value (as specified in the TTF file). Note that this will not 
+    * give the position of the farthest pixel, but it will give a pretty good idea of what area the 
+    * text will occupy. Tip: The text, when plotted unrotated, will fit approximately in a box with its lower left corner at 
+    * (x_start, y_start) and upper right at (x_start + width, y_start + size), where width is given by get_text_width()
+    * and size is the specified size of the text to be plotted. Tip: Text plotted at position
+    * (x_start, y_start), rotated with a given 'angle', and of a given 'size'
+    * whose width is 'width', will fit approximately inside a rectangle whose corners are at
+    *     1  (x_start, y_start)
+    *     2  (x_start + width*cos(angle), y_start + width*sin(angle))
+    *     3  (x_start + width*cos(angle) - size*sin(angle), y_start + width*sin(angle) + size*cos(angle))
+    *     4  (x_start - size*sin(angle), y_start + size*cos(angle))  
+    * */
+   
+   int get_text_width(char * face_path, int fontsize,  char * text);
+
+   int get_text_width_utf8(char * face_path, int fontsize, char * text);
+   
+   
+};
+
+
+#endif
+
diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt
new file mode 100644
index 00000000..9633ceb8
--- /dev/null
+++ b/libsrc/utils/CMakeLists.txt
@@ -0,0 +1,19 @@
+
+# Define the current source locations
+SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/utils)
+SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/utils)
+
+add_library(hyperion-utils
+		${CURRENT_HEADER_DIR}/RgbColor.h
+		${CURRENT_HEADER_DIR}/RgbImage.h
+
+		${CURRENT_SOURCE_DIR}/RgbColor.cpp
+		${CURRENT_SOURCE_DIR}/RgbImage.cpp
+
+		${CURRENT_HEADER_DIR}/jsonschema/JsonFactory.h
+		${CURRENT_HEADER_DIR}/jsonschema/JsonSchemaChecker.h
+		${CURRENT_SOURCE_DIR}/jsonschema/JsonSchemaChecker.cpp
+)
+
+target_link_libraries(hyperion-utils
+		jsoncpp)
diff --git a/libsrc/utils/RgbColor.cpp b/libsrc/utils/RgbColor.cpp
new file mode 100644
index 00000000..c26d3a45
--- /dev/null
+++ b/libsrc/utils/RgbColor.cpp
@@ -0,0 +1,10 @@
+
+// Local includes
+#include 
+
+RgbColor RgbColor::BLACK = {   0,   0,   0 };
+RgbColor RgbColor::RED   = { 255,   0,   0 };
+RgbColor RgbColor::GREEN = {   0, 255,   0 };
+RgbColor RgbColor::BLUE  = {   0,   0, 255 };
+RgbColor RgbColor::YELLOW= { 255, 255,   0 };
+RgbColor RgbColor::WHITE = { 255, 255, 255 };
diff --git a/libsrc/utils/RgbImage.cpp b/libsrc/utils/RgbImage.cpp
new file mode 100644
index 00000000..0b7bdd6b
--- /dev/null
+++ b/libsrc/utils/RgbImage.cpp
@@ -0,0 +1,51 @@
+
+// STL includes
+#include 
+#include 
+
+// hyperion Utils includes
+#include 
+
+
+RgbImage::RgbImage(const unsigned width, const unsigned height, const RgbColor background) :
+	mWidth(width),
+	mHeight(height),
+	mColors(NULL)
+{
+	mColors = new RgbColor[width*height];
+	for (RgbColor* color = mColors; color <= mColors+(mWidth*mHeight); ++color)
+	{
+		*color = background;
+	}
+}
+
+RgbImage::~RgbImage()
+{
+	delete[] mColors;
+}
+
+void RgbImage::setPixel(const unsigned x, const unsigned y, const RgbColor color)
+{
+	// Debug-mode sanity check on given index
+	(*this)(x,y) = color;
+}
+
+const RgbColor& RgbImage::operator()(const unsigned x, const unsigned y) const
+{
+	// Debug-mode sanity check on given index
+	assert(x < mWidth);
+	assert(y < mHeight);
+
+	const unsigned index = toIndex(x, y);
+	return mColors[index];
+}
+
+RgbColor& RgbImage::operator()(const unsigned x, const unsigned y)
+{
+	// Debug-mode sanity check on given index
+	assert(x < mWidth);
+	assert(y < mHeight);
+
+	const unsigned index = toIndex(x, y);
+	return mColors[index];
+}
diff --git a/libsrc/utils/jsonschema/JsonSchemaChecker.cpp b/libsrc/utils/jsonschema/JsonSchemaChecker.cpp
new file mode 100644
index 00000000..6b69ad0b
--- /dev/null
+++ b/libsrc/utils/jsonschema/JsonSchemaChecker.cpp
@@ -0,0 +1,460 @@
+// stdlib includes
+#include 
+#include 
+#include 
+#include 
+
+// Utils-Jsonschema includes
+#include 
+
+JsonSchemaChecker::JsonSchemaChecker()
+{
+	// empty
+}
+
+JsonSchemaChecker::~JsonSchemaChecker()
+{
+	// empty
+}
+
+bool JsonSchemaChecker::setSchema(const Json::Value & schema)
+{
+	_schema = schema;
+
+	// TODO: check the schema
+
+	return true;
+}
+
+bool JsonSchemaChecker::validate(const Json::Value & value)
+{
+	// initialize state
+	_error = false;
+	_messages.clear();
+	_currentPath.clear();
+	_currentPath.push_back("[root]");
+	_references.clear();
+
+	// collect dependencies
+	collectDependencies(value, _schema);
+
+	// validate
+	validate(value, _schema);
+
+	return !_error;
+}
+
+void JsonSchemaChecker::collectDependencies(const Json::Value & value, const Json::Value &schema)
+{
+	assert (schema.isObject());
+
+	// check if id is present
+	if (schema.isMember("id"))
+	{
+		// strore reference
+		assert (schema["id"].isString());
+		std::ostringstream ref;
+		ref << "$(" << schema["id"].asString() << ")";
+		_references[ref.str()] = &value;
+	}
+
+	// check the current json value
+	if (schema.isMember("properties"))
+	{
+		const Json::Value & properties = schema["properties"];
+		assert(properties.isObject());
+
+		for (Json::Value::const_iterator j = properties.begin(); j != properties.end(); ++j)
+		{
+			std::string property = j.memberName();
+			if (value.isMember(property))
+			{
+				collectDependencies(value[property], properties[property]);
+			}
+		}
+	}
+}
+
+void JsonSchemaChecker::validate(const Json::Value & value, const Json::Value &schema)
+{
+	assert (schema.isObject());
+
+	// check the current json value
+	for (Json::Value::const_iterator i = schema.begin(); i != schema.end(); ++i)
+	{
+		std::string attribute = i.memberName();
+		const Json::Value & attributeValue = *i;
+
+		if (attribute == "type")
+			checkType(value, attributeValue);
+		else if (attribute == "properties")
+			checkProperties(value, attributeValue);
+		else if (attribute == "additionalProperties")
+		{
+			// ignore the properties which are handled by the properties attribute (if present)
+			Json::Value::Members ignoredProperties;
+			if (schema.isMember("properties")) {
+				const Json::Value & props = schema["properties"];
+				ignoredProperties = props.getMemberNames();
+			}
+
+			checkAdditionalProperties(value, attributeValue, ignoredProperties);
+		}
+		else if (attribute == "dependencies")
+			checkDependencies(value, attributeValue);
+		else if (attribute == "minimum")
+			checkMinimum(value, attributeValue);
+		else if (attribute == "maximum")
+			checkMaximum(value, attributeValue);
+		else if (attribute == "items")
+			checkItems(value, attributeValue);
+		else if (attribute == "minItems")
+			checkMinItems(value, attributeValue);
+		else if (attribute == "maxItems")
+			checkMaxItems(value, attributeValue);
+		else if (attribute == "uniqueItems")
+			checkUniqueItems(value, attributeValue);
+		else if (attribute == "enum")
+			checkEnum(value, attributeValue);
+		else if (attribute == "required")
+			; // nothing to do. value is present so always oke
+		else if (attribute == "id")
+			; // references have already been collected
+		else
+		{
+			// no check function defined for this attribute
+			setMessage(std::string("No check function defined for attribute ") + attribute);
+			continue;
+		}
+	}
+}
+
+void JsonSchemaChecker::setMessage(const std::string & message)
+{
+	std::ostringstream oss;
+	std::copy(_currentPath.begin(), _currentPath.end(), std::ostream_iterator(oss, ""));
+	oss << ": " << message;
+	_messages.push_back(oss.str());
+}
+
+const std::list & JsonSchemaChecker::getMessages() const
+{
+	return _messages;
+}
+
+void JsonSchemaChecker::checkType(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isString());
+
+	std::string type = schema.asString();
+	bool wrongType = false;
+	if (type == "string")
+		wrongType = !value.isString();
+	else if (type == "number")
+		wrongType = !value.isNumeric();
+	else if (type == "integer")
+		wrongType = !value.isIntegral();
+	else if (type == "boolean")
+		wrongType = !value.isBool();
+	else if (type == "object")
+		wrongType = !value.isObject();
+	else if (type == "array")
+		wrongType = !value.isArray();
+	else if (type == "null")
+		wrongType = !value.isNull();
+	else if (type == "any")
+		wrongType = false;
+	else
+		assert(false);
+
+	if (wrongType)
+	{
+		_error = true;
+		setMessage(type + " expected");
+	}
+}
+
+void JsonSchemaChecker::checkProperties(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isObject());
+
+	if (!value.isObject())
+	{
+		_error = true;
+		setMessage("properies attribute is only valid for objects");
+		return;
+	}
+
+	for (Json::Value::const_iterator i = schema.begin(); i != schema.end(); ++i)
+	{
+		std::string property = i.memberName();
+		const Json::Value & propertyValue = *i;
+
+		assert(propertyValue.isObject());
+
+		_currentPath.push_back(std::string(".") + property);
+		if (value.isMember(property))
+		{
+			validate(value[property], propertyValue);
+		}
+		else if (propertyValue.get("required", false).asBool())
+		{
+			_error = true;
+			setMessage("missing member");
+		}
+		_currentPath.pop_back();
+	}
+}
+
+void JsonSchemaChecker::checkAdditionalProperties(const Json::Value & value, const Json::Value & schema, const Json::Value::Members & ignoredProperties)
+{
+	if (!value.isObject())
+	{
+		_error = true;
+		setMessage("additional properies attribute is only valid for objects");
+		return;
+	}
+
+	for (Json::Value::const_iterator i = value.begin(); i != value.end(); ++i)
+	{
+		std::string property = i.memberName();
+		if (std::find(ignoredProperties.begin(), ignoredProperties.end(), property) == ignoredProperties.end())
+		{
+			// property has no property definition. check against the definition for additional properties
+			_currentPath.push_back(std::string(".") + property);
+			if (schema.isBool())
+			{
+				if (schema.asBool() == false)
+				{
+					_error = true;
+					setMessage("no schema definition");
+				}
+			}
+			else
+			{
+				validate(value[property], schema);
+			}
+			_currentPath.pop_back();
+		}
+	}
+}
+
+void JsonSchemaChecker::checkDependencies(const Json::Value & value, const Json::Value & schemaLink)
+{
+	if (!value.isObject())
+	{
+		_error = true;
+		setMessage("dependencies attribute is only valid for objects");
+		return;
+	}
+
+	assert(schemaLink.isString());
+	std::map::iterator iter = _references.find(schemaLink.asString());
+	if (iter == _references.end())
+	{
+		_error = true;
+		std::ostringstream oss;
+		oss << "reference " << schemaLink.asString() << " could not be resolved";
+		setMessage(oss.str());
+		return;
+	}
+	const Json::Value & schema = *(iter->second);
+
+	std::list requiredProperties;
+	if (schema.isString())
+	{
+		requiredProperties.push_back(schema.asString());
+	}
+	else if (schema.isArray())
+	{
+		for (Json::UInt i = 0; i < schema.size(); ++i)
+		{
+			assert(schema[i].isString());
+			requiredProperties.push_back(schema[i].asString());
+		}
+	}
+	else
+	{
+		_error = true;
+		std::ostringstream oss;
+		oss << "Exepected reference " << schemaLink.asString() << " to resolve to a string or array";
+		setMessage(oss.str());
+		return;
+	}
+
+	for (std::list::const_iterator i = requiredProperties.begin(); i != requiredProperties.end(); ++i)
+	{
+		if (!value.isMember(*i))
+		{
+			_error = true;
+			std::ostringstream oss;
+			oss << "missing member " << *i;
+			setMessage(oss.str());
+		}
+	}
+}
+
+void JsonSchemaChecker::checkMinimum(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isNumeric());
+
+	if (!value.isNumeric())
+	{
+		// only for numeric
+		_error = true;
+		setMessage("minimum check only for numeric fields");
+		return;
+	}
+
+	if (value.asDouble() < schema.asDouble())
+	{
+		_error = true;
+		std::ostringstream oss;
+		oss << "value is too small (minimum=" << schema.asDouble() << ")";
+		setMessage(oss.str());
+	}
+}
+
+void JsonSchemaChecker::checkMaximum(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isNumeric());
+
+	if (!value.isNumeric())
+	{
+		// only for numeric
+		_error = true;
+		setMessage("maximum check only for numeric fields");
+		return;
+	}
+
+	if (value.asDouble() > schema.asDouble())
+	{
+		_error = true;
+		std::ostringstream oss;
+		oss << "value is too large (maximum=" << schema.asDouble() << ")";
+		setMessage(oss.str());
+	}
+}
+
+void JsonSchemaChecker::checkItems(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isObject());
+
+	if (!value.isArray())
+	{
+		// only for arrays
+		_error = true;
+		setMessage("items only valid for arrays");
+		return;
+	}
+
+	for(Json::ArrayIndex i = 0; i < value.size(); ++i)
+	{
+		// validate each item
+		std::ostringstream oss;
+		oss << "[" << i << "]";
+		_currentPath.push_back(oss.str());
+		validate(value[i], schema);
+		_currentPath.pop_back();
+	}
+}
+
+void JsonSchemaChecker::checkMinItems(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isIntegral());
+
+	if (!value.isArray())
+	{
+		// only for arrays
+		_error = true;
+		setMessage("minItems only valid for arrays");
+		return;
+	}
+
+	int minimum = schema.asInt();
+
+	if (static_cast(value.size()) < minimum)
+	{
+		_error = true;
+		std::ostringstream oss;
+		oss << "array is too small (minimum=" << minimum << ")";
+		setMessage(oss.str());
+	}
+}
+
+void JsonSchemaChecker::checkMaxItems(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isIntegral());
+
+	if (!value.isArray())
+	{
+		// only for arrays
+		_error = true;
+		setMessage("maxItems only valid for arrays");
+		return;
+	}
+
+	int maximum = schema.asInt();
+
+	if (static_cast(value.size()) > maximum)
+	{
+		_error = true;
+		std::ostringstream oss;
+		oss << "array is too large (maximum=" << maximum << ")";
+		setMessage(oss.str());
+	}
+}
+
+void JsonSchemaChecker::checkUniqueItems(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isBool());
+
+	if (!value.isArray())
+	{
+		// only for arrays
+		_error = true;
+		setMessage("maxItems only valid for arrays");
+		return;
+	}
+
+	if (schema.asBool() == true)
+	{
+		// make sure no two items are identical
+
+		for(Json::UInt i = 0; i < value.size(); ++i)
+		{
+			for (Json::UInt j = i+1; j < value.size(); ++j)
+			{
+				if (value[i] == value[j])
+				{
+					// found a value twice
+					_error = true;
+					setMessage("array must have unique values");
+				}
+			}
+		}
+	}
+}
+
+void JsonSchemaChecker::checkEnum(const Json::Value & value, const Json::Value & schema)
+{
+	assert(schema.isArray());
+
+	for(Json::ArrayIndex i = 0; i < schema.size(); ++i)
+	{
+		if (schema[i] == value)
+		{
+			// found enum value. done.
+			return;
+		}
+	}
+
+	// nothing found
+	_error = true;
+	std::ostringstream oss;
+	oss << "Unknown enum value (allowed values are: ";
+	std::string values = Json::FastWriter().write(schema);
+	oss << values.substr(0, values.size()-1); // The writer append a new line which we don't want
+	oss << ")";
+	setMessage(oss.str());
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..ae953450
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,44 @@
+
+# Find the libPNG
+find_package(PNG REQUIRED QUIET)
+
+# Add additional includes dirs
+include_directories(${PNG_INCLUDE_DIR})
+
+# Add the simple test executable 'TestSpi'
+add_executable(TestSpi
+		TestSpi.cpp)
+
+target_link_libraries(TestSpi
+		hyperion
+		hyperion-utils)
+
+add_executable(TestHyperionPng
+		TestHyperionPng.cpp)
+
+target_link_libraries(TestHyperionPng
+		hyperion-png)
+
+add_executable(WriteConfig
+		WriteConfig.cpp)
+
+add_executable(TestConfigFile
+		TestConfigFile.cpp)
+
+target_link_libraries(TestConfigFile
+		hyperion-utils
+		hyperion)
+
+add_executable(ViewPng
+		ViewPng.cpp)
+
+target_link_libraries(ViewPng
+		hyperion
+		hyperion-utils
+		${PNG_LIBRARIES})
+
+add_executable(Test2BobLight
+		Test2BobLight.cpp)
+
+target_link_libraries(Test2BobLight
+		bob2hyperion)
diff --git a/src/FbWriter.h b/src/FbWriter.h
new file mode 100644
index 00000000..9cd4afdc
--- /dev/null
+++ b/src/FbWriter.h
@@ -0,0 +1,115 @@
+#pragma once
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+class FbWriter
+{
+public:
+	FbWriter()
+	{
+		initialise();
+	}
+
+	~FbWriter()
+	{
+		if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &orig_vinfo))
+		{
+			printf("Error re-setting variable information.\n");
+		}
+
+		close(fbfd);
+	}
+
+	int initialise()
+	{
+		// Open the file for reading and writing
+		fbfd = open("/dev/fb0", O_RDWR);
+		if (!fbfd)
+		{
+			printf("Error: cannot open framebuffer device.\n");
+			return(-1);
+		}
+		printf("The framebuffer device was opened successfully.\n");
+
+		// Get fixed screen information
+		if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
+		{
+			printf("Error reading fixed information.\n");
+		}
+		// Get variable screen information
+		if (ioctl(fbfd, FBIOGET_VSCREENINFO, &orig_vinfo))
+		{
+			printf("Error reading variable information.\n");
+		}
+		printf("Original %dx%d, %dbpp\n", orig_vinfo.xres, orig_vinfo.yres, orig_vinfo.bits_per_pixel );
+
+
+		return 0;
+	}
+
+	void writeImage(const RgbImage& image)
+	{
+		std::cout << "Writing image [" << image.width() << "x" << image.height() << "]" << std::endl;
+
+		// Make a copy of the original screen-info
+		fb_var_screeninfo vinfo = orig_vinfo;
+//		memcpy(&vinfo, &orig_vinfo, sizeof(fb_var_screeninfo));
+
+		// Configure the frame-buffer for the new image
+		vinfo.xres = image.width();
+		vinfo.yres = image.height();
+		vinfo.bits_per_pixel = 24;
+		if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo))
+		{
+			printf("Error configuring frame-buffer");
+		}
+
+		ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
+		std::cout << "New set resolution: " << vinfo.xres << "x" << vinfo.yres << std::endl;
+
+		// map fb to user mem
+		long screensize = vinfo.yres * finfo.line_length;//vinfo.yres * vinfo.bits_per_pixel / 8;
+		char* fbp = (char*)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd,  0);
+		if (!fbp)
+		{
+			// Failed to create memory map
+			std::cout << "Failed to create the memory map" << std::endl;
+			return;
+		}
+		std::cout << "Screensize  : " << screensize << std::endl;
+		std::cout << "Max fb-index: " << (image.width()-1)*3 + (image.height()-1)*finfo.line_length << std::endl;
+		std::cout << "[" << vinfo.xres << "x" << vinfo.yres << "] == [" << image.width() << "x" << image.height() << "]" << std::endl;
+
+		for (unsigned iY=0; iY
+#include 
+
+#include 
+
+// Boblight includes
+#include 
+
+int main()
+{
+	void* boblight_ptr = boblight_init();
+	if (!boblight_ptr)
+	{
+		std::cerr << "Failed to initialise bob-light" << std::endl;
+		return -1;
+	}
+
+	const unsigned width  = 112;
+	const unsigned height = 63;
+
+	boblight_setscanrange(boblight_ptr, width, height);
+
+	std::vector rgbColor = { 255, 255, 0 };
+	for (unsigned iY=0; iY
+
+// Boblight includes
+#include 
+
+int main()
+{
+	std::cout << "TestBoblightOrig started" << std::endl;
+
+	std::cout << "TestBoblightOrig finished" << std::endl;
+
+	return 0;
+}
+
diff --git a/src/TestConfigFile.cpp b/src/TestConfigFile.cpp
new file mode 100644
index 00000000..cb330de3
--- /dev/null
+++ b/src/TestConfigFile.cpp
@@ -0,0 +1,38 @@
+
+// STL includes
+#include 
+
+// JsonSchema includes
+#include 
+
+// hyperion includes
+#include 
+
+int main()
+{
+	std::string homeDir = getenv("RASPILIGHT_HOME");
+
+	const std::string schemaFile = homeDir + "/hyperion.schema.json";
+	const std::string configFile = homeDir + "/hyperion.config.json";
+
+	Json::Value config;
+	if (JsonFactory::load(schemaFile, configFile, config) < 0)
+	{
+		std::cerr << "UNABLE TO LOAD CONFIGURATION" << std::endl;
+		return -1;
+	}
+
+	const Json::Value& deviceConfig = config["device"];
+	const Json::Value& ledConfig    = config["leds"];
+	const Json::Value& colorConfig  = config["color"];
+
+	std::cout << "COLOR CONFIGURATION: " << colorConfig.toStyledString() << std::endl;
+
+	const Json::Value& redConfig = colorConfig["red"];
+	double redGamma = redConfig["gamma"].asDouble();
+	std::cout << "RED GAMMA = " << redGamma << std::endl;
+	std::cout << "RED GAMMA = " << colorConfig["red.gamma"].asDouble() << std::endl;
+	LedString ledString = LedString::construct(ledConfig, colorConfig);
+
+	return 0;
+}
diff --git a/src/TestHyperionPng.cpp b/src/TestHyperionPng.cpp
new file mode 100644
index 00000000..39da58d3
--- /dev/null
+++ b/src/TestHyperionPng.cpp
@@ -0,0 +1,43 @@
+
+// STL includes
+#include 
+
+// Boblight includes
+#include 
+
+int main()
+{
+	std::cout << "Initialisaing Boblight" << std::endl;
+	void* blPng = boblight_init();
+
+	int width  = 112;
+	int height = 64;
+	std::cout << "Defining scan range (" << width << "x" << height << ")" << std::endl;
+	boblight_setscanrange(blPng, width, height);
+
+	int colorPtr[3];
+	colorPtr[0] = 255;
+	colorPtr[1] = 0;
+	colorPtr[2] = 0;
+	std::cout << "Using color [" << colorPtr[0] << "; " << colorPtr[1] << "; " << colorPtr[2] << "]" << std::endl;
+
+	int nrOfFrames = 150;
+	std::cout << "Generating " << nrOfFrames << " frames" << std::endl;
+	for (int iFrame=0; iFrame
+#include 
+#include 
+#include 
+#include 
+#include 
+
+// Local includes
+#include 
+
+#include "../libsrc/hyperion/LedDeviceWs2801.h"
+
+void setColor(char* colorStr)
+{
+	RgbColor color = RgbColor::BLACK;
+	std::cout << "Switching all leds to: ";
+	if (strncmp("red", colorStr, 3) == 0)
+	{
+		std::cout << "red";
+		color = RgbColor::RED;
+	}
+	else if (strncmp("green", colorStr, 5) == 0)
+	{
+		std::cout << "green";
+		color = RgbColor::GREEN;
+	}
+	else if (strncmp("blue", colorStr, 5) == 0)
+	{
+		std::cout << "blue";
+		color = RgbColor::BLUE;
+	}
+	else if (strncmp("cyan", colorStr, 5) == 0)
+	{
+		std::cout << "cyan";
+	}
+	else if (strncmp("gray", colorStr, 4) == 0)
+	{
+		std::cout << "gray";
+	}
+	else if (strncmp("white", colorStr, 5) == 0)
+	{
+		std::cout << "white";
+		color = RgbColor::WHITE;
+	}
+	else if (strncmp("black", colorStr, 5) == 0)
+	{
+		std::cout << "black";
+		color = RgbColor::BLACK;
+	}
+	std::cout << std::endl;
+
+	unsigned ledCnt = 50;
+	std::vector buff(ledCnt, color);
+
+	LedDeviceWs2801 ledDevice("SpiPi", "/dev/spidev0.0", 20000, 40000);
+	ledDevice.open();
+	ledDevice.write(buff);
+}
+
+bool _running = true;
+void doCircle()
+{
+	RgbColor color_1 = RgbColor::RED;
+	RgbColor color_2 = RgbColor::YELLOW;
+
+	unsigned ledCnt = 50;
+	std::vector data(ledCnt, RgbColor::BLACK);
+
+	LedDeviceWs2801 ledDevice("SpiPi", "/dev/spidev0.0", 20000, 40000);
+	ledDevice.open();
+
+	timespec loopTime;
+	loopTime.tv_sec  = 0;
+	loopTime.tv_nsec = 100000000; // 100 ms
+
+	int curLed_1  = 0;
+	int nextLed_1 = 1;
+
+	int curLed_2  = 49;
+	int nextLed_2 = 48;
+
+
+	while (_running)
+	{
+		data[curLed_1] = RgbColor::BLACK;
+		data[curLed_2] = RgbColor::BLACK;
+
+		// Move the current and the next pointer
+		curLed_1 = nextLed_1;
+		curLed_2 = nextLed_2;
+		++nextLed_1;
+		--nextLed_2;
+		if (nextLed_1 == int(ledCnt))
+		{
+			nextLed_1 = 0;
+		}
+		if (nextLed_2 < 0)
+		{
+			nextLed_2 = 49;
+		}
+
+		data[curLed_1] = color_1;
+
+		data[curLed_2] = color_2;
+
+		ledDevice.write(data);
+
+		nanosleep(&loopTime, NULL);
+	}
+
+	// Switch the current leds off
+	data[curLed_1] = RgbColor::BLACK;
+	data[curLed_2] = RgbColor::BLACK;
+
+	ledDevice.write(data);
+}
+
+#include 
+
+void signal_handler(int signum)
+{
+	_running = false;
+}
+
+int main(int argc, char** argv)
+{
+	if (sizeof(RgbColor) != 3)
+	{
+		std::cout << "sizeof(RgbColor) = " << sizeof(RgbColor) << std::endl;
+		return -1;
+	}
+
+	// Install signal handlers to stop loops
+	signal(SIGTERM, &signal_handler);
+	signal(SIGINT,  &signal_handler);
+
+	if (argc < 2)
+	{
+		std::cerr << "Missing argument" << std::endl;
+		return -1;
+	}
+
+	if (strncmp("fixed", argv[1], 5) == 0)
+	{
+		setColor(argv[2]);
+		return 0;
+	}
+	else if (strncmp("circle", argv[1], 6) == 0)
+	{
+		doCircle();
+	}
+	else
+	{
+		std::cerr << "Unknown option: " << argv[1] << std::endl;
+	}
+
+	return 0;
+}
+
diff --git a/src/ViewPng.cpp b/src/ViewPng.cpp
new file mode 100644
index 00000000..917a4976
--- /dev/null
+++ b/src/ViewPng.cpp
@@ -0,0 +1,155 @@
+
+// STL includes
+#include 
+
+// LibPNG includes
+#include 
+
+// Utils includes
+#include 
+#include 
+
+// Raspilight includes
+#include 
+
+// Local includes
+#include "FbWriter.h"
+
+bool read_png(std::string file_name, RgbImage*& rgbImage)
+{
+	png_structp png_ptr;
+	png_infop info_ptr;
+	FILE *fp;
+
+	if ((fp = fopen(file_name.c_str(), "rb")) == NULL)
+	{
+		return false;
+	}
+
+	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+	if (png_ptr == NULL)
+	{
+		fclose(fp);
+		return false;
+	}
+
+	info_ptr = png_create_info_struct(png_ptr);
+	if (info_ptr == NULL)
+	{
+		fclose(fp);
+		png_destroy_read_struct(&png_ptr, NULL, NULL);
+		return false;
+	}
+
+	if (setjmp(png_jmpbuf(png_ptr)))
+	{
+		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+		fclose(fp);
+		return false;
+	}
+
+	png_init_io(png_ptr, fp);
+
+	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_SWAP_ALPHA | PNG_TRANSFORM_EXPAND, NULL);
+
+	png_uint_32 width = png_get_image_width(png_ptr, info_ptr);
+	png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+
+	png_uint_32 bitdepth   = png_get_bit_depth(png_ptr, info_ptr);
+	png_uint_32 channels   = png_get_channels(png_ptr, info_ptr);
+	png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
+
+	std::cout << "BitDepth" << std::endl;
+	std::cout << bitdepth << std::endl;
+	std::cout << "Channels" << std::endl;
+	std::cout << channels << std::endl;
+	std::cout << "ColorType" << std::endl;
+	std::cout << color_type << std::endl;
+
+	png_bytepp row_pointers;
+	row_pointers = png_get_rows(png_ptr, info_ptr);
+
+	rgbImage = new RgbImage(width, height);
+
+	for (unsigned iRow=0; iRow(row_pointers[iRow]);
+			for (unsigned iCol=0; iColsetPixel(iCol, iRow, rowPtr[iCol]);
+			}
+		}
+		else if (color_type == PNG_COLOR_TYPE_RGBA)
+		{
+			unsigned* rowPtr = reinterpret_cast(row_pointers[iRow]);
+			for (unsigned iCol=0; iColsetPixel(iCol, iRow, {uint8_t((argbValue >> 16) & 0xFF), uint8_t((argbValue >> 8) & 0xFF), uint8_t((argbValue) & 0xFF)});
+			}
+		}
+		else
+		{
+			// Unknown/Unimplemented color-format
+			return false;
+		}
+	}
+
+	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+	fclose(fp);
+
+	return true;
+}
+
+int main(int argc, char** argv)
+{
+	if (argc < 2)
+	{
+		// Missing required argument
+		std::cout << "Missing PNG-argumet. Usage: 'ViewPng [png-file]" << std::endl;
+		return 0;
+	}
+
+	const std::string pngFilename = argv[1];
+
+	RgbImage* image = nullptr;
+	if (!read_png(pngFilename, image) || image == nullptr)
+	{
+		std::cout << "Failed to load image" << std::endl;
+		return -1;
+	}
+
+	const char* homeDir = getenv("RASPILIGHT_HOME");
+	if (!homeDir)
+	{
+		homeDir = "/home/pi";
+	}
+
+	std::cout << "RASPILIGHT HOME DIR: " << homeDir << std::endl;
+
+	const std::string schemaFile = std::string(homeDir) + "/hyperion.schema.json";
+	const std::string configFile = std::string(homeDir) + "/hyperion.config.json";
+
+	Json::Value raspiConfig;
+	if (JsonFactory::load(schemaFile, configFile, raspiConfig) < 0)
+	{
+		std::cerr << "UNABLE TO LOAD CONFIGURATION" << std::endl;
+		return -1;
+	}
+	std::cout << "Loaded configuration: " << raspiConfig << std::endl;
+
+	FbWriter fbWriter;
+
+	Hyperion raspiLight(raspiConfig);
+	raspiLight.setInputSize(image->width(), image->height());
+
+	fbWriter.writeImage(*image);
+	raspiLight(*image);
+
+	sleep(5);
+
+	delete image;
+}
diff --git a/src/WriteConfig.cpp b/src/WriteConfig.cpp
new file mode 100644
index 00000000..3278821b
--- /dev/null
+++ b/src/WriteConfig.cpp
@@ -0,0 +1,198 @@
+// STL includes
+#include 
+#include 
+#include 
+#include 
+#include 
+
+using namespace std;
+
+ofstream& indent(ofstream& ofs, unsigned indent)
+{
+	for (unsigned i=0; i createLedMapping(const LedFrame ledFrame)
+{
+	std::vector leds;
+	for (unsigned iLed=0; iLed leds = createLedMapping(myFrame);
+	ofstream configOfs(filename.c_str());
+
+	configOfs << "// Automatically generated configuration file for 'Hyperion'" << endl;
+	configOfs << "// Generation script: " << argv[0] << endl;
+	configOfs << endl;
+	configOfs << "{" << endl;
+
+	// Write the device section
+	indent(configOfs, 1) << R"("device" : )" << endl;
+	indent(configOfs, 1) << "{" << endl;
+	indent(configOfs, 2) << R"("name"     : "MyPi",)" << endl;
+	indent(configOfs, 2) << R"("type"     : "ws2801",)" << endl;
+	indent(configOfs, 2) << R"("output"   : "/dev/spidev0.0",)" << endl;
+	indent(configOfs, 2) << R"("interval" : 20000,)" << endl;
+	indent(configOfs, 2) << R"("rate"     : 48000)" << endl;
+	indent(configOfs, 1) << "}," << endl;
+
+	// Write the color-correction section
+	indent(configOfs, 1) << R"("color" : )" << endl;
+	indent(configOfs, 1) << "{" << endl;
+	indent(configOfs, 2) << R"("red" : )" << endl;
+	indent(configOfs, 2) << "{" << endl;
+	indent(configOfs, 3) << R"("gamma"      : 1.0,)" << endl;
+	indent(configOfs, 3) << R"("adjust"     : 1.0,)" << endl;
+	indent(configOfs, 3) << R"("blacklevel" : 0.0)" << endl;
+	indent(configOfs, 2) << "}," << endl;
+	indent(configOfs, 2) << R"("green" : )" << endl;
+	indent(configOfs, 2) << "{" << endl;
+	indent(configOfs, 3) << R"("gamma"      : 1.0,)" << endl;
+	indent(configOfs, 3) << R"("adjust"     : 1.0,)" << endl;
+	indent(configOfs, 3) << R"("blacklevel" : 0.0)" << endl;
+	indent(configOfs, 2) << "}," << endl;
+	indent(configOfs, 2) << R"("blue" : )" << endl;
+	indent(configOfs, 2) << "{" << endl;
+	indent(configOfs, 3) << R"("gamma"      : 1.0,)" << endl;
+	indent(configOfs, 3) << R"("adjust"     : 1.0,)" << endl;
+	indent(configOfs, 3) << R"("blacklevel" : 0.0)" << endl;
+	indent(configOfs, 2) << "}" << endl;
+	indent(configOfs, 1) << "}," << endl;
+
+	// Write the leds section
+	indent(configOfs, 1) << R"("leds" : )" << endl;
+	indent(configOfs, 1) << "[" << endl;
+	for (const Led& led : leds)
+	{
+		// Add comments indicating the corners of the configuration
+		if (led.minX == 0.0)
+		{
+			if (led.minY == 0.0)
+			{
+				indent(configOfs, 2) << "// TOP-LEFT Corner" << endl;
+			}
+			else if (led.maxY == 100.0)
+			{
+				indent(configOfs, 2) << "// BOTTOM-LEFT Corner" << endl;
+			}
+		}
+		else if (led.maxX == 100.0)
+		{
+			if (led.minY == 0.0)
+			{
+				indent(configOfs, 2) << "// TOP-RIGHT Corner" << endl;
+			}
+			else if (led.maxY == 100.0)
+			{
+				indent(configOfs, 2) << "// BOTTOM-RIGHT Corner" << endl;
+			}
+		}
+
+		// Write the configuration of the led
+		indent(configOfs, 2) << "{" << endl;
+		indent(configOfs, 3) << R"("index" : )" << led.index << "," << endl;
+		indent(configOfs, 3) << R"("hscan" : { "minimum" : )" << led.minX << R"(, "maximum" : )" << led.maxX << R"( },)" << endl;
+		indent(configOfs, 3) << R"("vscan" : { "minimum" : )" << led.minY << R"(, "maximum" : )" << led.maxY << R"( })" << endl;
+		indent(configOfs, 2) << "}";
+		if (led.index != leds.back().index)
+		{
+			configOfs << ",";
+		}
+		configOfs << endl;
+	}
+	indent(configOfs, 1) << "]" << endl;
+
+	configOfs << "}" << endl;
+}