First working version with some test executables

This commit is contained in:
T. van der Zwan 2013-07-26 20:38:34 +00:00
commit 10b5b80675
62 changed files with 15621 additions and 0 deletions

38
CMakeLists.txt Normal file
View File

@ -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)

24
bin/install_hyperion.sh Normal file
View File

@ -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

63
cmake/FindPNG.cmake Normal file
View File

@ -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 )

291
config/Hyperion.config.json Normal file
View File

@ -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 }
}
]
}

134
config/Hyperion.schema.json Normal file
View File

@ -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
}
}
}
}
}
}
}
}

9
dependencies/CMakeLists.txt vendored Normal file
View File

@ -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)

9
dependencies/build/CMakeLists.txt vendored Normal file
View File

@ -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)

1
dependencies/build/jsoncpp/AUTHORS vendored Normal file
View File

@ -0,0 +1 @@
Baptiste Lepilleur <blep@users.sourceforge.net>

View File

@ -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)

55
dependencies/build/jsoncpp/LICENSE vendored Normal file
View File

@ -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.

View File

@ -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 <stdlib.h>
# include <assert.h>
# 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<typename AllocatedType
,const unsigned int objectPerAllocation>
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<BatchInfo*>( 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

View File

@ -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<Value **>( newIndexes );
}
virtual void releaseArrayPageIndex( Value **indexes,
ValueInternalArray::PageIndex indexCount )
{
if ( indexes )
free( indexes );
}
virtual Value *allocateArrayPage()
{
return static_cast<Value *>( 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<Value **>( newIndexes );
}
virtual void releaseArrayPageIndex( Value **indexes,
ValueInternalArray::PageIndex indexCount )
{
if ( indexes )
free( indexes );
}
virtual Value *allocateArrayPage()
{
return static_cast<Value *>( pagesAllocator_.allocate() );
}
virtual void releaseArrayPage( Value *value )
{
if ( value )
pagesAllocator_.release( value );
}
private:
BatchAllocator<ValueInternalArray,1> arraysAllocator_;
BatchAllocator<Value,ValueInternalArray::itemsPerPage> 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<ValueInternalArray *>( this );
it.currentItemIndex_ = 0;
it.currentPageIndex_ = pages_;
}
void
ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const
{
it.array_ = const_cast<ValueInternalArray *>( 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

View File

@ -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<ValueInternalMap,1> mapsAllocator_;
BatchAllocator<ValueInternalLink,1> 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 &current->items_[index];
}
}
return 0;
}
Value *
ValueInternalMap::find( const char *key )
{
const ValueInternalMap *constThis = this;
return const_cast<Value *>( 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 = &current->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<ValueInternalMap *>( this );
it.bucketIndex_ = 0;
it.itemIndex_ = 0;
it.link_ = buckets_;
}
void
ValueInternalMap::makeEndIterator( IteratorState &it ) const
{
it.map_ = const_cast<ValueInternalMap *>( 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

View File

@ -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 <json/reader.h>
# include <json/value.h>
# include "json_tool.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <utility>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <iostream>
#include <stdexcept>
#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<char> begin(sin);
//std::istream_iterator<char> 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_ = &currentValue();
}
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 &current,
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 &current,
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

93
dependencies/build/jsoncpp/json_tool.h vendored Normal file
View File

@ -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<char>(cp);
}
else if (cp <= 0x7FF)
{
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
}
else if (cp <= 0xFFFF)
{
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
}
else if (cp <= 0x10FFFF)
{
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(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 *&current )
{
*--current = 0;
do
{
*--current = char(value % 10) + '0';
value /= 10;
}
while ( value != 0 );
}
} // namespace Json {
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED

1829
dependencies/build/jsoncpp/json_value.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 &current )
: 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 &current )
: 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 &current )
: 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

View File

@ -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 <json/writer.h>
# include "json_tool.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <utility>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#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 </
// sequence.
// Should add a flag to allow this compatibility mode and prevent this
// sequence from occurring.
default:
if ( isControlCharacter( *c ) )
{
std::ostringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*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

8
dependencies/build/jsoncpp/sconscript vendored Normal file
View File

@ -0,0 +1,8 @@
Import( 'env buildLibrary' )
buildLibrary( env, Split( """
json_reader.cpp
json_value.cpp
json_writer.cpp
""" ),
'json' )

24
dependencies/include/json/autolink.h vendored Normal file
View File

@ -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 <cpptl/cpptl_autolink.h>
# 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

96
dependencies/include/json/config.h vendored Normal file
View File

@ -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 <cpptl/config.h>
# 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

49
dependencies/include/json/features.h vendored Normal file
View File

@ -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

44
dependencies/include/json/forwards.h vendored Normal file
View File

@ -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

15
dependencies/include/json/json.h vendored Normal file
View File

@ -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

214
dependencies/include/json/reader.h vendored Normal file
View File

@ -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 <deque>
# include <stack>
# include <string>
# include <iostream>
namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a> 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<ErrorInfo> 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 &current,
Location end,
unsigned int &unicode );
bool decodeUnicodeEscapeSequence( Token &token,
Location &current,
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 &currentValue();
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<Value *> 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

1103
dependencies/include/json/value.h vendored Normal file

File diff suppressed because it is too large Load Diff

185
dependencies/include/json/writer.h vendored Normal file
View File

@ -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 <vector>
# include <string>
# include <iostream>
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 <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a> 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<std::string> ChildValues;
ChildValues childValues_;
std::string document_;
std::string indentString_;
int rightMargin_;
int indentSize_;
bool addChildValues_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> 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 <a HREF="http://www.json.org">JSON</a> 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<std::string> 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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
//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));

103
include/boblight.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
//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 <dlfcn.h>
#include <stddef.h>
//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<value>
#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

View File

@ -0,0 +1,41 @@
#pragma once
// hyperion-utils includes
#include <utils/RgbImage.h>
#include <hyperion/LedString.h>
#include <hyperion/ImageToLedsMap.h>
#include <hyperion/LedDevice.h>
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<RgbColor>& colors) const;
LedString mLedString;
RgbImage* mImage;
ImageToLedsMap mLedsMap;
LedDevice* mDevice;
};

View File

@ -0,0 +1,27 @@
#pragma once
// hyperion-utils includes
#include <utils/RgbImage.h>
// hyperion includes
#include <hyperion/LedString.h>
class ImageToLedsMap
{
public:
ImageToLedsMap();
void createMapping(const RgbImage& image, const std::vector<Led>& leds);
std::vector<RgbColor> getMeanLedColor();
RgbColor findMeanColor(const std::vector<const RgbColor*>& colors);
std::vector<RgbColor> getMedianLedColor();
RgbColor findMedianColor(std::vector<const RgbColor*>& colors);
private:
std::vector<std::vector<const RgbColor*> > mColorsMap;
};

View File

@ -0,0 +1,27 @@
#pragma once
// STL incldues
#include <vector>
// Utility includes
#include <utils/RgbColor.h>
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<RgbColor>& ledValues) = 0;
};

View File

@ -0,0 +1,72 @@
#pragma once
// STL includes
#include <ctime>
#include <string>
#include <vector>
// Local includes
#include <utils/RgbColor.h>
// 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.
* <pre>
* |--------------------image--|
* | minX maxX |
* | |-----|maxY |
* | | | |
* | |-----|minY |
* | |
* | |
* | |
* |---------------------------|
* <endpre>
*/
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<Led>& leds() const;
private:
std::vector<Led> 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;
};

31
include/utils/RgbColor.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
// STL includes
#include <stdint.h>
#include <iostream>
// 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;
}

56
include/utils/RgbImage.h Normal file
View File

@ -0,0 +1,56 @@
#pragma once
#include <cassert>
#include <cstring>
// 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;
};

View File

@ -0,0 +1,66 @@
#pragma once
#include <string>
#include <istream>
#include <fstream>
#include <stdexcept>
#include <sstream>
// JSON-Schema includes
#include <utils/jsonschema/JsonSchemaChecker.h>
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;
}
};

View File

@ -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 <string>
#include <list>
// jsoncpp includes
#include <json/json.h>
/**
* 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<std::string> & 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<std::string> _currentPath;
std::list<std::string> _messages;
bool _error;
std::map<std::string, const Json::Value *> _references; // ref 2 value
};

15
libsrc/CMakeLists.txt Normal file
View File

@ -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)

138
libsrc/bob2hyperion.cpp Normal file
View File

@ -0,0 +1,138 @@
// SysLog includes
#include <syslog.h>
// Boblight includes
#include "boblight.h"
// JsonSchema includes
#include <utils/jsonschema/JsonFactory.h>
// Raspilight includes
#include <hyperion/Hyperion.h>
static std::ofstream sDebugStream;
inline Hyperion* rasp_cast(void* hyperion_ptr)
{
return reinterpret_cast<Hyperion*>(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<void*>(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;
}

View File

@ -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
)

View File

@ -0,0 +1,101 @@
// Syslog include
#include <syslog.h>
// JsonSchema include
#include <utils/jsonschema/JsonFactory.h>
// hyperion include
#include <hyperion/Hyperion.h>
#include <hyperion/LedDevice.h>
#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<RgbColor> 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<RgbColor> ledColors = mLedsMap.getMeanLedColor();
std::vector<RgbColor> 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<RgbColor>(mLedString.leds().size(), color));
}
void Hyperion::applyTransform(std::vector<RgbColor>& 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;
}
}

View File

@ -0,0 +1,90 @@
// STL includes
#include <algorithm>
// hyperion includes
#include <hyperion/ImageToLedsMap.h>
ImageToLedsMap::ImageToLedsMap()
{
// empty
}
void ImageToLedsMap::createMapping(const RgbImage& image, const std::vector<Led>& leds)
{
mColorsMap.resize(leds.size(), std::vector<const RgbColor*>());
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 && y<image.height(); ++y)
{
for (unsigned x = minX_idx; x<=maxX_idx && x<image.width(); ++x)
{
ledColors->push_back(&image(x,y));
}
}
}
}
std::vector<RgbColor> ImageToLedsMap::getMeanLedColor()
{
std::vector<RgbColor> 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<const RgbColor*>& 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<RgbColor> ImageToLedsMap::getMedianLedColor()
{
std::vector<RgbColor> ledColors;
for (std::vector<const RgbColor*>& colors : mColorsMap)
{
const RgbColor color = findMedianColor(colors);
ledColors.push_back(color);
}
return ledColors;
}
RgbColor ImageToLedsMap::findMedianColor(std::vector<const RgbColor*>& 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};
}

View File

@ -0,0 +1,85 @@
// STL includes
#include <cstring>
#include <cstdio>
#include <iostream>
// Linux includes
#include <fcntl.h>
#include <sys/ioctl.h>
// 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<RgbColor> &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;
}

View File

@ -0,0 +1,33 @@
#pragma once
// STL includes
#include <string>
// Linux-SPI includes
#include <linux/spi/spidev.h>
// hyperion incluse
#include <hyperion/LedDevice.h>
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<RgbColor> &ledValues);
private:
const std::string mDeviceName;
const int mBaudRate_Hz;
int mFid;
spi_ioc_transfer spi;
timespec latchTime;
};

View File

@ -0,0 +1,60 @@
// STL includes
#include <cstring>
#include <unistd.h>
#include <iostream>
// Json includes
#include <json/json.h>
// hyperion includes
#include <hyperion/LedString.h>
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<Led>& LedString::leds() const
{
return mLeds;
}

View File

@ -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})

View File

@ -0,0 +1,174 @@
// STL includes
#include <fstream>
#include <sstream>
#include <iostream>
// Boblight includes
#include <boblight.h>
// 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<void*>(raspiPng);
}
void boblight_destroy(void* vpboblight)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(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<RaspiPng*>(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<RaspiPng*>(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<RaspiPng*>(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<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
int boblight_setpriority(void* vpboblight, int priority)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
const char* boblight_geterror(void* vpboblight)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return "ERROR";
}
int boblight_getnrlights(void* vpboblight)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return 50;
}
const char* boblight_getlightname(void* vpboblight, int lightnr)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return "LIGHT";
}
int boblight_getnroptions(void* vpboblight)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
const char* boblight_getoptiondescript(void* vpboblight, int option)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return "";
}
int boblight_setoption(void* vpboblight, int lightnr, const char* option)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(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<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
int boblight_addpixel(void* vpboblight, int lightnr, int* rgb)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
int boblight_ping(void* vpboblight, int* outputused)
{
RaspiPng* raspiPng = reinterpret_cast<RaspiPng*>(vpboblight);
raspiPng->logFile << __PRETTY_FUNCTION__ << std::endl;
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <png.h>
// 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 <ft2build.h>
#include FT_FREETYPE_H
#endif
#ifdef OLD_CPP // For compatibility with older compilers.
#include <iostream.h>
#include <math.h>
#include <wchar.h>
#include <string.h>
using namespace std;
#endif // from ifdef OLD_CPP
#ifndef OLD_CPP // Default situation.
#include <iostream>
#include <cmath>
#include <cwchar>
#include <string>
#endif // from ifndef OLD_CPP
//png.h must be included before FreeType headers.
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#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
* ( <gurkan@linuks.mine.nu>, 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

View File

@ -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)

10
libsrc/utils/RgbColor.cpp Normal file
View File

@ -0,0 +1,10 @@
// Local includes
#include <utils/RgbColor.h>
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 };

51
libsrc/utils/RgbImage.cpp Normal file
View File

@ -0,0 +1,51 @@
// STL includes
#include <cassert>
#include <cstring>
// hyperion Utils includes
#include <utils/RgbImage.h>
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];
}

View File

@ -0,0 +1,460 @@
// stdlib includes
#include <cassert>
#include <iterator>
#include <sstream>
#include <algorithm>
// Utils-Jsonschema includes
#include <utils/jsonschema/JsonSchemaChecker.h>
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<std::string>(oss, ""));
oss << ": " << message;
_messages.push_back(oss.str());
}
const std::list<std::string> & 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<std::string, const Json::Value *>::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<std::string> 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<std::string>::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<int>(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<int>(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());
}

44
src/CMakeLists.txt Normal file
View File

@ -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)

115
src/FbWriter.h Normal file
View File

@ -0,0 +1,115 @@
#pragma once
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <utils/RgbImage.h>
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<image.height(); ++iY)
{
memcpy(fbp + iY*finfo.line_length, &(image(0, iY)), image.width()*3);
// for (unsigned iX=0; iX<image.width(); ++iX)
// {
// const unsigned pixOffset = iX*3 + iY*finfo.line_length;
// fbp[pixOffset ] = image(iX, iY).red;
// fbp[pixOffset+1] = image(iX, iY).green;
// fbp[pixOffset+2] = image(iX, iY).blue;
// }
}
std::cout << "FINISHED COPYING IMAGE TO FRAMEBUFFER" << std::endl;
// cleanup
munmap(fbp, screensize);
}
// The identifier of the FrameBuffer File-Device
int fbfd;
// The 'Fixed' screen information
fb_fix_screeninfo finfo;
// The original 'Variable' screen information
fb_var_screeninfo orig_vinfo;
};

39
src/Test2BobLight.cpp Normal file
View File

@ -0,0 +1,39 @@
// STL includes
#include <iostream>
#include <vector>
#include <unistd.h>
// Boblight includes
#include <boblight.h>
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<int> rgbColor = { 255, 255, 0 };
for (unsigned iY=0; iY<height; ++iY)
{
for (unsigned iX=0; iX<width; ++iX)
{
boblight_addpixelxy(boblight_ptr, iX, iY, rgbColor.data());
}
}
boblight_sendrgb(boblight_ptr, 0, nullptr);
//sleep(5);
boblight_destroy(boblight_ptr);
return 0;
}

16
src/TestBoblightOrig.cpp Normal file
View File

@ -0,0 +1,16 @@
// STL includes
#include <iostream>
// Boblight includes
#include <boblight.h>
int main()
{
std::cout << "TestBoblightOrig started" << std::endl;
std::cout << "TestBoblightOrig finished" << std::endl;
return 0;
}

38
src/TestConfigFile.cpp Normal file
View File

@ -0,0 +1,38 @@
// STL includes
#include <cstdlib>
// JsonSchema includes
#include <utils/jsonschema/JsonFactory.h>
// hyperion includes
#include <hyperion/LedString.h>
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;
}

43
src/TestHyperionPng.cpp Normal file
View File

@ -0,0 +1,43 @@
// STL includes
#include <iostream>
// Boblight includes
#include <boblight.h>
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<nrOfFrames; ++iFrame)
{
for (int iWidth=0; iWidth<width; ++iWidth)
{
for (int iHeight=0; iHeight<height; ++iHeight)
{
boblight_addpixelxy(blPng, iWidth, iHeight, colorPtr);
}
}
boblight_sendrgb(blPng, 0, NULL);
}
std::cout << "Destroying Boblight" << std::endl;
boblight_destroy(blPng);
return 0;
}

161
src/TestSpi.cpp Normal file
View File

@ -0,0 +1,161 @@
// STL includes
#include <vector>
#include <ctime>
#include <cstring>
#include <string>
#include <unistd.h>
#include <iostream>
// Local includes
#include <utils/RgbColor.h>
#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<RgbColor> 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<RgbColor> 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 <csignal>
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;
}

155
src/ViewPng.cpp Normal file
View File

@ -0,0 +1,155 @@
// STL includes
#include <iostream>
// LibPNG includes
#include <png.h>
// Utils includes
#include <utils/RgbImage.h>
#include <utils/jsonschema/JsonFactory.h>
// Raspilight includes
#include <hyperion/Hyperion.h>
// 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<height; ++iRow)
{
if (color_type == PNG_COLOR_TYPE_RGB)
{
RgbColor* rowPtr = reinterpret_cast<RgbColor*>(row_pointers[iRow]);
for (unsigned iCol=0; iCol<width; ++iCol)
{
rgbImage->setPixel(iCol, iRow, rowPtr[iCol]);
}
}
else if (color_type == PNG_COLOR_TYPE_RGBA)
{
unsigned* rowPtr = reinterpret_cast<unsigned*>(row_pointers[iRow]);
for (unsigned iCol=0; iCol<width; ++iCol)
{
const unsigned argbValue = rowPtr[iCol];
rgbImage->setPixel(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;
}

198
src/WriteConfig.cpp Normal file
View File

@ -0,0 +1,198 @@
// STL includes
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
ofstream& indent(ofstream& ofs, unsigned indent)
{
for (unsigned i=0; i<indent; ++i)
{
ofs << '\t';
}
return ofs;
}
struct LedFrame
{
unsigned topLedCnt = 17;
unsigned rightLedCnt = 8;
unsigned bottomLedCnt = 17;
unsigned leftLedCnt = 8;
unsigned topLeftOffset = 17;
unsigned borderWidth = 10;
unsigned borderHeight = 10;
unsigned totalLedCnt() const
{
return topLeftOffset + rightLedCnt + bottomLedCnt + leftLedCnt;
}
};
struct Led
{
unsigned index;
double minX;
double maxX;
double minY;
double maxY;
};
void determineBoundaries(const LedFrame& ledFrame, const unsigned iLed, double& minX, double& maxX, double& minY, double& maxY)
{
if (iLed < ledFrame.topLedCnt)
{
// TOP of screen
minX = iLed * 100.0/ledFrame.topLedCnt;
maxX = (iLed+1) * 100.0/ledFrame.topLedCnt;
minY = 100.0 - ledFrame.borderHeight;
maxY = 100.0;
}
else if (iLed < (ledFrame.topLedCnt+ledFrame.rightLedCnt))
{
// RIGHT of screen
minX = 100.0 - ledFrame.borderWidth;
maxX = 100.0;
minY = 100.0 - (iLed-15) * 100.0/(ledFrame.rightLedCnt+2);
maxY = 100.0 - (iLed-16) * 100.0/(ledFrame.rightLedCnt+2);
}
else if (iLed < (ledFrame.topLedCnt+ledFrame.rightLedCnt+ledFrame.bottomLedCnt))
{
// BOTTOM of screen
minX = 100.0 - (iLed-24) * 100.0/ledFrame.bottomLedCnt;
maxX = 100.0 - (iLed-25) * 100.0/ledFrame.bottomLedCnt;
minY = 0.0;
maxY = ledFrame.borderHeight;
}
else if (iLed < (ledFrame.topLedCnt+ledFrame.rightLedCnt+ledFrame.bottomLedCnt+ledFrame.leftLedCnt))
{
// LEFT of screen
minX = 0.0;
maxX = ledFrame.borderWidth;
minY = (iLed-41) * 100.0/(ledFrame.leftLedCnt+2);
maxY = (iLed-40) * 100.0/(ledFrame.leftLedCnt+2);
}
else
{
std::cerr << "Requested led index(" << iLed << ") out of bound" << endl;
}
}
std::vector<Led> createLedMapping(const LedFrame ledFrame)
{
std::vector<Led> leds;
for (unsigned iLed=0; iLed<ledFrame.totalLedCnt(); ++iLed)
{
Led led;
led.index = (iLed + ledFrame.topLeftOffset)%ledFrame.totalLedCnt();
determineBoundaries(ledFrame, iLed, led.minX, led.maxX, led.minY, led.maxY);
leds.push_back(led);
}
std::sort(leds.begin(), leds.end(), [](const Led& lhs, const Led& rhs){ return lhs.index < rhs.index; });
return leds;
}
int main(int argc, char** argv)
{
const std::string filename = "hyperion.config.json";
LedFrame myFrame;
std::vector<Led> 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;
}