mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
First working version with some test executables
This commit is contained in:
15
libsrc/CMakeLists.txt
Normal file
15
libsrc/CMakeLists.txt
Normal 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
138
libsrc/bob2hyperion.cpp
Normal 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;
|
||||
}
|
17
libsrc/hyperion/CMakeLists.txt
Normal file
17
libsrc/hyperion/CMakeLists.txt
Normal 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
|
||||
)
|
101
libsrc/hyperion/Hyperion.cpp
Normal file
101
libsrc/hyperion/Hyperion.cpp
Normal 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;
|
||||
}
|
||||
}
|
90
libsrc/hyperion/ImageToLedsMap.cpp
Normal file
90
libsrc/hyperion/ImageToLedsMap.cpp
Normal 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};
|
||||
}
|
85
libsrc/hyperion/LedDeviceWs2801.cpp
Normal file
85
libsrc/hyperion/LedDeviceWs2801.cpp
Normal 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;
|
||||
}
|
33
libsrc/hyperion/LedDeviceWs2801.h
Normal file
33
libsrc/hyperion/LedDeviceWs2801.h
Normal 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;
|
||||
};
|
60
libsrc/hyperion/LedString.cpp
Normal file
60
libsrc/hyperion/LedString.cpp
Normal 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;
|
||||
}
|
19
libsrc/hyperionpng/CMakeLists.txt
Normal file
19
libsrc/hyperionpng/CMakeLists.txt
Normal 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})
|
174
libsrc/hyperionpng/hyperion-png.cpp
Normal file
174
libsrc/hyperionpng/hyperion-png.cpp
Normal 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;
|
||||
}
|
4722
libsrc/hyperionpng/pngwriter.cc
Normal file
4722
libsrc/hyperionpng/pngwriter.cc
Normal file
File diff suppressed because it is too large
Load Diff
745
libsrc/hyperionpng/pngwriter.h
Normal file
745
libsrc/hyperionpng/pngwriter.h
Normal 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
|
||||
|
19
libsrc/utils/CMakeLists.txt
Normal file
19
libsrc/utils/CMakeLists.txt
Normal 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
10
libsrc/utils/RgbColor.cpp
Normal 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
51
libsrc/utils/RgbImage.cpp
Normal 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];
|
||||
}
|
460
libsrc/utils/jsonschema/JsonSchemaChecker.cpp
Normal file
460
libsrc/utils/jsonschema/JsonSchemaChecker.cpp
Normal 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());
|
||||
}
|
Reference in New Issue
Block a user