diff --git a/.travis-functions.sh b/.travis-functions.sh new file mode 100755 index 0000000..8ad5fda --- /dev/null +++ b/.travis-functions.sh @@ -0,0 +1,120 @@ +#!/bin/bash + +# +# .travis-functions.sh: +# - helper functions to be sourced from .travis.yml +# - designed to respect travis' environment but testing locally is possible +# + +if [ ! -f "src/CMakeLists.txt" ]; then + echo ".travis-functions.sh must be sourced from source dir" >&2 + return 1 || exit 1 +fi + +# some config settings +MAKE="make -j2" + +function xconfigure +{ + local CMAKEOPT + + if test "$USE_QT" = "qt4"; then + CMAKEOPT+=" -DDI_FORCE_QT4=ON" + fi + cmake $CMAKEOPT ../src +} + +function script_xvfb_run +{ + # start xvfb X server and give it some time to start + export DISPLAY=:99.0 + if [ "${TRAVIS_OS_NAME}" = "osx" ]; then + sudo Xvfb :99 -ac -screen 0 1024x768x24 & + else + Xvfb :99 -ac -screen 0 1024x768x24 & + fi + sleep 3 + + # just for info output + glewinfo + + # Run the program. + # + # For now we just start it in background and see if it's still running. Once + # it's fixed for OSX and Ubuntu soft-GL we could use paraameters like + # --screenshot ../test/data/surfR_p_dist.ply ../test/data/distR.labels + # Note, OSX Xvfb has GL Version 4.1, so it should be possible somehow ... + + ./bin/DirectionalityIndicator & + sleep 10 + + # kill returns failure if there is no process running + kill $! +} + +function script_generic +{ + xconfigure || return + $MAKE || return + script_xvfb_run || return +} + +function install_deps_linux +{ + # install some packages from Ubuntu's default sources + sudo apt-get -qq update + + # we should better install the glewinfo version from our bundled sources + sudo apt-get install -qq \ + glew-utils \ + || return + + if test "$USE_QT" = "qt4"; then + sudo apt-get install -qq \ + libqt4-dev \ + libqt4-opengl-dev \ + || return + else + sudo apt-get install -qq \ + qt5-default \ + libqt5opengl5-dev \ + || return + fi +} + +function install_deps_osx +{ + brew update >/dev/null + brew install \ + glew \ + qt5 \ + || return + + QT_DIR=$(brew --prefix qt5) + PATH="$QT_DIR/bin:$PATH" +} + +function travis_install_script +{ + if [ "$TRAVIS_OS_NAME" = "osx" ]; then + install_deps_osx || return + else + install_deps_linux || return + fi +} + +function travis_script +{ + local ret + + # cd causes ugly traces on OSX, thats why we do it here + cd build || return + + set -o xtrace + + script_generic + ret=$? + + set +o xtrace + return $ret +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b0e085f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,42 @@ +language: cpp + +sudo: required +dist: trusty + +os: + - linux + +compiler: + - gcc + - clang + +env: + - USE_QT="qt4" + - USE_QT="qt5" + +matrix: + include: + - os: osx + osx_image: xcode8.3 + compiler: clang + env: USE_QT="qt5" + - os: osx + osx_image: xcode7.3 + compiler: clang + env: USE_QT="qt5" + +install: + - env | grep -v "encrypted" | LC_ALL=C sort + - lscpu || sysctl hw + - source ./.travis-functions.sh + - travis_install_script + +script: + - travis_script + +branches: + only: + - master + - next + - /^travis.*/ + - /^tmp.*/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 408324f..2bdcace 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -282,22 +282,19 @@ IF( NOT REQUIRE_QT4 ) MESSAGE( "Using custom Qt path. Ensure you set the path to the directory containing the bin and lib directories." ) SET( CMAKE_PREFIX_PATH "$ENV{DI_QTDIR}/lib/cmake/Qt5Widgets" ${CMAKE_PREFIX_PATH} ) SET( CMAKE_PREFIX_PATH "$ENV{DI_QTDIR}/lib/cmake/Qt5OpenGL" ${CMAKE_PREFIX_PATH} ) - SET( CMAKE_PREFIX_PATH "$ENV{DI_QTDIR}/lib/cmake/Qt5WebKitWidgets" ${CMAKE_PREFIX_PATH} ) SET( CMAKE_PREFIX_PATH $ENV{DI_QTDIR} ${CMAKE_PREFIX_PATH} ) endif() # Package dependencies: FIND_PACKAGE( Qt5Widgets ) FIND_PACKAGE( Qt5OpenGL ) - FIND_PACKAGE( Qt5WebKitWidgets ) # Qt5 specific setup - IF( Qt5Widgets_FOUND AND Qt5OpenGL_FOUND AND Qt5WebKitWidgets_FOUND ) + IF( Qt5Widgets_FOUND AND Qt5OpenGL_FOUND ) # Includes: INCLUDE_DIRECTORIES( SYSTEM ${QT_INCLUDE_DIR} ) INCLUDE_DIRECTORIES( SYSTEM ${Qt5Widgets_INCLUDE_DIRS} ) INCLUDE_DIRECTORIES( SYSTEM ${Qt5OpenGL_INCLUDE_DIRS} ) - INCLUDE_DIRECTORIES( SYSTEM ${Qt5WebKitWidgets_INCLUDE_DIRS} ) # Compiling with Qt5 requires some special definitions and flags to be set. @@ -305,7 +302,6 @@ IF( NOT REQUIRE_QT4 ) SET( _QT5_DEFINITIONS "" ) LIST( APPEND _QT5_DEFINITIONS ${Qt5Widgets_DEFINITIONS} ) LIST( APPEND _QT5_DEFINITIONS ${Qt5OpenGL_DEFINITIONS} ) - LIST( APPEND _QT5_DEFINITIONS ${Qt5WebKitWidgets_DEFINITIONS} ) LIST( REMOVE_DUPLICATES _QT5_DEFINITIONS ) ADD_DEFINITIONS( ${_QT5_DEFINITIONS} ) @@ -313,11 +309,10 @@ IF( NOT REQUIRE_QT4 ) SET( _QT5_EXECUTABLE_COMPILE_FLAGS "" ) LIST( APPEND _QT5_EXECUTABLE_COMPILE_FLAGS ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS} ) LIST( APPEND _QT5_EXECUTABLE_COMPILE_FLAGS ${Qt5OpenGL_EXECUTABLE_COMPILE_FLAGS} ) - LIST( APPEND _QT5_EXECUTABLE_COMPILE_FLAGS ${Qt5WebKitWidgets_EXECUTABLE_COMPILE_FLAGS} ) LIST( REMOVE_DUPLICATES _QT5_EXECUTABLE_COMPILE_FLAGS ) SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_QT5_EXECUTABLE_COMPILE_FLAGS}" ) - SET( QT_Link_Libs Qt5::Widgets Qt5::OpenGL Qt5::WebKitWidgets ) + SET( QT_Link_Libs Qt5::Widgets Qt5::OpenGL ) ELSE() # Not Found ... we need at least Qt4: SET( REQUIRE_QT4 TRUE ) @@ -326,14 +321,14 @@ ENDIF() IF( REQUIRE_QT4 ) # Searching Qt4 - FIND_PACKAGE( Qt4 4.8.0 REQUIRED QtCore QtGui QtOpenGL QtWebKit ) + FIND_PACKAGE( Qt4 4.8.0 REQUIRED QtCore QtGui QtOpenGL) IF( NOT QT4_FOUND ) MESSAGE( FATAL_ERROR "Neither Qt5 nor Qt4 were found. Abort. Try using DI_QTDIR or QTDIR environment variables to point to your Qt installation." ) ENDIF() INCLUDE_DIRECTORIES( SYSTEM ${QT_INCLUDES} ) - SET( QT_Link_Libs Qt4::QtCore Qt4::QtGui Qt4::QtOpenGL Qt4::QtWebKit ) + SET( QT_Link_Libs Qt4::QtCore Qt4::QtGui Qt4::QtOpenGL ) endif() # This is needed since the mocs will be generated there diff --git a/src/app/App.h b/src/app/App.h index cf8890a..9753be7 100644 --- a/src/app/App.h +++ b/src/app/App.h @@ -129,7 +129,7 @@ namespace di * * \return true if everything is ok */ - virtual bool handleCommandLine( const std::vector< std::string >& arguments, int argc, char** argv ); + virtual bool handleCommandLine( const std::vector< std::string >& arguments, int argc, char** argv ) override; private: /** diff --git a/src/lib/di/algorithms/RenderIllustrativeLines.h b/src/lib/di/algorithms/RenderIllustrativeLines.h index 49fa282..648a476 100644 --- a/src/lib/di/algorithms/RenderIllustrativeLines.h +++ b/src/lib/di/algorithms/RenderIllustrativeLines.h @@ -68,7 +68,7 @@ namespace di /** * Process the data in the inputs and update output data. Keep in mind that this might be called in its own thread thread. */ - virtual void process(); + virtual void process() override; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Visualization Specific Methods @@ -79,7 +79,7 @@ namespace di * * \note this runs in the OpenGL thread and the context is bound. */ - virtual void prepare(); + virtual void prepare() override; /** * Finalize your OpenGL resources here. Free buffers and shaders. @@ -87,7 +87,7 @@ namespace di * * \note this runs in the OpenGL thread and the context is bound. */ - virtual void finalize(); + virtual void finalize() override; /** * Do actual rendering. @@ -97,7 +97,7 @@ namespace di * * \param view the view to render to. This contains probably useful information. */ - virtual void render( const core::View& view ); + virtual void render( const core::View& view ) override; /** * This method is called between the frames. Use this to update resources. Immediately return if nothing needs to update. If you do not @@ -109,7 +109,7 @@ namespace di * \param view the view to render to. This contains probably useful information. * \param reload to force a reload of all resources. */ - virtual void update( const core::View& view, bool reload = false ); + virtual void update( const core::View& view, bool reload = false ) override; /** * Each visualization needs to know the rendering area it will use. In most cases, this is the bounding box of the rendered geometry. @@ -117,7 +117,7 @@ namespace di * * \return bounding box of this visualization */ - virtual core::BoundingBox getBoundingBox() const; + virtual core::BoundingBox getBoundingBox() const override; protected: /** diff --git a/src/lib/di/core/CommandQueue.cpp b/src/lib/di/core/CommandQueue.cpp index 43e1777..765adbd 100644 --- a/src/lib/di/core/CommandQueue.cpp +++ b/src/lib/di/core/CommandQueue.cpp @@ -44,74 +44,51 @@ namespace di void CommandQueue::run() { - // Used to lock the command queue mutex - std::unique_lock< std::mutex > lock( m_commandQueueMutex ); + bool running = true; // loop and handle ... - while( m_running ) + while( running ) { - if( m_commandQueue.empty() ) + // Mutex lock to pull/copy all guarded variables. + std::unique_lock< std::mutex > lock( m_commandQueueMutex ); + bool gracefulStop; + std::list< SPtr< Command > > pullCommands; + + // Only wait if there is nothing to do. It's a loop to + // handle spurious wakeups as well. + while( m_commandQueue.empty() && m_running ) { LogD << "Empty queue. Sleeping." << LogEnd; + m_commandQueueCond.wait( lock ); } + running = m_running; + gracefulStop = m_gracefulStop; + m_commandQueue.swap( pullCommands ); - // was the thread notified again? - if( !m_notified ) - { - // No. We wait. - m_commandQueueCond.wait( lock, - [ this ] // keep waiting if not explicitly notified - { - return m_notified || !m_commandQueue.empty(); - } - ); - } - m_notified = false; - // stop? - if( !m_running ) - { - // if stopping, process the remaining commands: - for( auto command : m_commandQueue ) - { - // be fool-proof - if( !command ) - { - continue; - } - - // If we stop gracefully, we allow each command to be processed. - if( m_gracefulStop ) - { - processCommand( command ); - } - else // for a forced stop, we abort the remaining commands - { - command->abort(); - } - } - // Cleanup ... - m_commandQueue.clear(); - } - else // business as usual ... process command + // From here we are unlocked to process all pulled commands. + lock.unlock(); + + LogD << "Wakeup " << pullCommands.size() << " commands" + << (running ? "." : " and exit.") << LogEnd; + + for( auto command : pullCommands ) { - // get command - SPtr< Command > command; - if( !m_commandQueue.empty() ) - { - command = m_commandQueue.front(); - m_commandQueue.pop_front(); - } // be fool-proof if( !command ) { continue; } - // unlock as this would (otherwise) block the queue itself when "processCommand" is blocking - m_commandQueueMutex.unlock(); - // process - processCommand( command ); + // If we stop gracefully, we allow each command to be processed. + if( running || gracefulStop ) + { + processCommand( command ); + } + else // for a forced stop, we abort the remaining commands + { + command->abort(); + } } } } @@ -145,11 +122,11 @@ namespace di void CommandQueue::start() { + std::lock_guard< std::mutex > lock( m_commandQueueMutex ); + // ignore the call if the thread is running already if( m_thread ) - { return; - } // start the std::thread. m_running = true; @@ -158,20 +135,24 @@ namespace di void CommandQueue::stop( bool graceful ) { + std::unique_lock< std::mutex > lock( m_commandQueueMutex ); + + // ignore the call if there is no thread running + if( !m_thread ) + return; + m_running = false; m_gracefulStop = graceful; - if( m_thread ) - { - notifyThread(); - m_thread->join(); - m_thread = nullptr; - } + + lock.unlock(); + + notifyThread(); + m_thread->join(); + m_thread = nullptr; } void CommandQueue::notifyThread() { - // Always set this variable to ensure that a busy thread continues to work if it is not waiting on m_commandQueueCond right now. - m_notified = true; m_commandQueueCond.notify_one(); } } diff --git a/src/lib/di/core/CommandQueue.h b/src/lib/di/core/CommandQueue.h index 06cbdd3..b6bdd5d 100644 --- a/src/lib/di/core/CommandQueue.h +++ b/src/lib/di/core/CommandQueue.h @@ -121,7 +121,8 @@ namespace di std::list< SPtr< Command > > m_commandQueue; /** - * Securing the command queue during processing. + * Securing the command queue during processing. This mutex protects + * m_commandQueue, m_running and m_gracefulStop. */ std::mutex m_commandQueueMutex; @@ -145,11 +146,6 @@ namespace di */ void notifyThread(); - /** - * If true, the thread was notified in the past (before the wait call). - */ - bool m_notified = false; - /** * Handle a single command. * diff --git a/src/lib/di/core/data/GridTransformation.h b/src/lib/di/core/data/GridTransformation.h index 19b25b9..4f3da9d 100644 --- a/src/lib/di/core/data/GridTransformation.h +++ b/src/lib/di/core/data/GridTransformation.h @@ -58,7 +58,7 @@ namespace di * * \param other the instance to copy */ - GridTransformation( const GridTransformation& other ) = default; + GridTransformation( const GridTransformation& ) = default; /** * Assign operation. @@ -67,7 +67,7 @@ namespace di * * \return this. */ - GridTransformation& operator=( const GridTransformation& other ) = default; + GridTransformation& operator=( const GridTransformation& ) = default; /** * Destructor. Clean up if needed. diff --git a/src/lib/di/ext/bitmap_image.hpp b/src/lib/di/ext/bitmap_image.hpp index 13d14a9..6cb4f83 100644 --- a/src/lib/di/ext/bitmap_image.hpp +++ b/src/lib/di/ext/bitmap_image.hpp @@ -1,21 +1,21 @@ /* -*************************************************************************** -* * -* Platform Independent * -* Bitmap Image Reader Writer Library * -* * -* Author: Arash Partow - 2002 * -* URL: http://partow.net/programming/bitmap/index.html * -* * -* Note: This library only supports 24-bits per pixel bitmap format files. * -* * -* Copyright notice: * -* Free use of the Platform Independent Bitmap Image Reader Writer Library * -* is permitted under the guidelines and in accordance with the most * -* current version of the Common Public License. * -* http://www.opensource.org/licenses/cpl1.0.php * -* * -*************************************************************************** + ***************************************************************************** + * * + * Platform Independent * + * Bitmap Image Reader Writer Library * + * * + * Author: Arash Partow - 2002 * + * URL: http://partow.net/programming/bitmap/index.html * + * * + * Note: This library only supports 24-bits per pixel bitmap format files. * + * * + * Copyright notice: * + * Free use of the Platform Independent Bitmap Image Reader Writer Library * + * is permitted under the guidelines and in accordance with the most current * + * version of the MIT License. * + * http://www.opensource.org/licenses/MIT * + * * + ***************************************************************************** */ @@ -25,11 +25,13 @@ #include #include #include +#include #include #include #include #include #include +#include class bitmap_image @@ -47,25 +49,20 @@ class bitmap_image red_plane = 2 }; - bitmap_image() : file_name_(""), - data_ (0), - length_(0), - width_ (0), - height_(0), - row_increment_(0), + width_ (0), + height_ (0), + row_increment_ (0), bytes_per_pixel_(3), channel_mode_(bgr_mode) {} bitmap_image(const std::string& filename) : file_name_(filename), - data_ (0), - length_(0), - width_ (0), - height_(0), - row_increment_(0), + width_ (0), + height_ (0), + row_increment_ (0), bytes_per_pixel_(0), channel_mode_(bgr_mode) { @@ -74,33 +71,25 @@ class bitmap_image bitmap_image(const unsigned int width, const unsigned int height) : file_name_(""), - data_ (0), - length_(0), - width_(width), + width_ (width ), height_(height), - row_increment_(0), + row_increment_ (0), bytes_per_pixel_(3), channel_mode_(bgr_mode) { - create_bitmap(); + create_bitmap(); } bitmap_image(const bitmap_image& image) : file_name_(image.file_name_), - data_(0), - width_(image.width_), - height_(image.height_), - row_increment_(0), + width_ (image.width_ ), + height_ (image.height_ ), + row_increment_ (0), bytes_per_pixel_(3), channel_mode_(bgr_mode) { create_bitmap(); - std::copy(image.data_, image.data_ + image.length_, data_); - } - - ~bitmap_image() - { - delete [] data_; + data_ = image.data_; } bitmap_image& operator=(const bitmap_image& image) @@ -114,7 +103,7 @@ class bitmap_image row_increment_ = 0; channel_mode_ = image.channel_mode_; create_bitmap(); - std::copy(image.data_, image.data_ + image.length_, data_); + data_ = image.data_; } return *this; @@ -122,16 +111,15 @@ class bitmap_image inline bool operator!() { - return (data_ == 0) || - (length_ == 0) || - (width_ == 0) || - (height_ == 0) || - (row_increment_== 0); + return (data_.size() == 0) || + (width_ == 0) || + (height_ == 0) || + (row_increment_ == 0); } inline void clear(const unsigned char v = 0x00) { - std::fill(data_,data_ + length_,v); + std::fill(data_.begin(),data_.end(),v); } inline unsigned char red_channel(const unsigned int x, const unsigned int y) const @@ -166,7 +154,7 @@ class bitmap_image inline unsigned char* row(unsigned int row_index) const { - return data_ + (row_index * row_increment_); + return const_cast(&data_[(row_index * row_increment_)]); } inline void get_pixel(const unsigned int x, const unsigned int y, @@ -176,11 +164,19 @@ class bitmap_image { const unsigned int y_offset = y * row_increment_; const unsigned int x_offset = x * bytes_per_pixel_; + blue = data_[y_offset + x_offset + 0]; green = data_[y_offset + x_offset + 1]; red = data_[y_offset + x_offset + 2]; } + template + inline void get_pixel(const unsigned int x, const unsigned int y, + RGB& colour) + { + get_pixel(x, y, colour.red, colour.green, colour.blue); + } + inline void set_pixel(const unsigned int x, const unsigned int y, const unsigned char red, const unsigned char green, @@ -188,11 +184,18 @@ class bitmap_image { const unsigned int y_offset = y * row_increment_; const unsigned int x_offset = x * bytes_per_pixel_; + data_[y_offset + x_offset + 0] = blue; data_[y_offset + x_offset + 1] = green; data_[y_offset + x_offset + 2] = red; } + template + inline void set_pixel(const unsigned int x, const unsigned int y, const RGB& colour) + { + set_pixel(x, y, colour.red, colour.green, colour.blue); + } + inline bool copy_from(const bitmap_image& image) { if ( @@ -203,7 +206,8 @@ class bitmap_image return false; } - std::copy(image.data_,image.data_ + image.length_,data_); + data_ = image.data_; + return true; } @@ -219,16 +223,18 @@ class bitmap_image unsigned char* itr1 = row(y + y_offset) + x_offset * bytes_per_pixel_; const unsigned char* itr2 = source_image.row(y); const unsigned char* itr2_end = itr2 + source_image.width_ * bytes_per_pixel_; + std::copy(itr2,itr2_end,itr1); } + return true; } - inline bool region(const unsigned int& x, - const unsigned int& y, - const unsigned int& width, + inline bool region(const unsigned int& x , + const unsigned int& y , + const unsigned int& width , const unsigned int& height, - bitmap_image& dest_image) + bitmap_image& dest_image ) { if ((x + width ) > width_ ) { return false; } if ((y + height) > height_) { return false; } @@ -246,39 +252,52 @@ class bitmap_image unsigned char* itr1 = row(r + y) + x * bytes_per_pixel_; unsigned char* itr1_end = itr1 + (width * bytes_per_pixel_); unsigned char* itr2 = dest_image.row(r); + std::copy(itr1,itr1_end,itr2); } return true; } - inline bool set_region(const unsigned int& x, - const unsigned int& y, - const unsigned int& width, - const unsigned int& height, - const unsigned char& value) + inline bool roi_from_center(const unsigned int& cx , + const unsigned int& cy , + const unsigned int& width , + const unsigned int& height, + bitmap_image& dest_image ) + { + return region(cx - (width / 2), cy - (height / 2), + width, height, + dest_image); + } + + inline bool set_region(const unsigned int& x , + const unsigned int& y , + const unsigned int& width , + const unsigned int& height, + const unsigned char& value ) { - if ((x + width) > width_) { return false; } + if ((x + width ) > width_ ) { return false; } if ((y + height) > height_) { return false; } for (unsigned int r = 0; r < height; ++r) { unsigned char* itr = row(r + y) + x * bytes_per_pixel_; unsigned char* itr_end = itr + (width * bytes_per_pixel_); + std::fill(itr,itr_end,value); } return true; } - inline bool set_region(const unsigned int& x, - const unsigned int& y, - const unsigned int& width, - const unsigned int& height, - const color_plane color, - const unsigned char& value) + inline bool set_region(const unsigned int& x , + const unsigned int& y , + const unsigned int& width , + const unsigned int& height, + const color_plane color , + const unsigned char& value ) { - if ((x + width) > width_) { return false; } + if ((x + width ) > width_ ) { return false; } if ((y + height) > height_) { return false; } const unsigned int color_plane_offset = offset(color); @@ -298,15 +317,15 @@ class bitmap_image return true; } - inline bool set_region(const unsigned int& x, - const unsigned int& y, - const unsigned int& width, - const unsigned int& height, - const unsigned char& red, - const unsigned char& green, - const unsigned char& blue) + inline bool set_region(const unsigned int& x , + const unsigned int& y , + const unsigned int& width , + const unsigned int& height, + const unsigned char& red , + const unsigned char& green , + const unsigned char& blue ) { - if ((x + width) > width_) { return false; } + if ((x + width ) > width_ ) { return false; } if ((y + height) > height_) { return false; } for (unsigned int r = 0; r < height; ++r) @@ -316,27 +335,46 @@ class bitmap_image while (itr != itr_end) { - *(itr++) = blue; + *(itr++) = blue; *(itr++) = green; - *(itr++) = red; + *(itr++) = red; } } return true; } - void reflective_image(bitmap_image& image) + void reflective_image(bitmap_image& image, const bool include_diagnols = false) { - image.setwidth_height(3 * width_, 3 * height_,true); - image.copy_from(*this,width_,height_); + image.setwidth_height(3 * width_, 3 * height_, true); + + image.copy_from(*this, width_, height_); + vertical_flip(); - image.copy_from(*this,width_,0); - image.copy_from(*this,width_,2 * height_); + + image.copy_from(*this, width_, 0); + image.copy_from(*this, width_, 2 * height_); + vertical_flip(); horizontal_flip(); - image.copy_from(*this,0,height_); - image.copy_from(*this,2 * width_,height_); + + image.copy_from(*this, 0, height_); + image.copy_from(*this, 2 * width_, height_); + horizontal_flip(); + + if (include_diagnols) + { + bitmap_image tile = *this; + + tile.vertical_flip(); + tile.horizontal_flip(); + + image.copy_from(tile, 0, 0); + image.copy_from(tile, 2 * width_, 0); + image.copy_from(tile, 2 * width_, 2 * height_); + image.copy_from(tile, 0 , 2 * height_); + } } inline unsigned int width() const @@ -363,8 +401,7 @@ class bitmap_image const unsigned int height, const bool clear = false) { - delete[] data_; - data_ = 0; + data_.clear(); width_ = width; height_ = height; @@ -372,51 +409,53 @@ class bitmap_image if (clear) { - std::fill(data_,data_ + length_,0x00); + std::fill(data_.begin(),data_.end(),static_cast(0x00)); } } - void save_image(const std::string& file_name) + void save_image(const std::string& file_name) const { std::ofstream stream(file_name.c_str(),std::ios::binary); if (!stream) { - std::cout << "bitmap_image::save_image(): Error - Could not open file " << file_name << " for writing!" << std::endl; + std::cerr << "bitmap_image::save_image(): Error - Could not open file " << file_name << " for writing!" << std::endl; return; } - bitmap_file_header bfh; bitmap_information_header bih; bih.width = width_; bih.height = height_; bih.bit_count = static_cast(bytes_per_pixel_ << 3); - bih.clr_important = 0; - bih.clr_used = 0; - bih.compression = 0; - bih.planes = 1; - bih.size = 40; - bih.x_pels_per_meter = 0; - bih.y_pels_per_meter = 0; + bih.clr_important = 0; + bih.clr_used = 0; + bih.compression = 0; + bih.planes = 1; + bih.size = bih.struct_size(); + bih.x_pels_per_meter = 0; + bih.y_pels_per_meter = 0; bih.size_image = (((bih.width * bytes_per_pixel_) + 3) & 0x0000FFFC) * bih.height; - bfh.type = 19778; - bfh.size = 55 + bih.size_image; - bfh.reserved1 = 0; - bfh.reserved2 = 0; - bfh.off_bits = bih.struct_size() + bfh.struct_size(); + bitmap_file_header bfh; + + bfh.type = 19778; + bfh.size = bfh.struct_size() + bih.struct_size() + bih.size_image; + bfh.reserved1 = 0; + bfh.reserved2 = 0; + bfh.off_bits = bih.struct_size() + bfh.struct_size(); write_bfh(stream,bfh); write_bih(stream,bih); unsigned int padding = (4 - ((3 * width_) % 4)) % 4; - char padding_data[4] = {0x0,0x0,0x0,0x0}; + char padding_data[4] = { 0x00, 0x00, 0x00, 0x00 }; for (unsigned int i = 0; i < height_; ++i) { - unsigned char* data_ptr = data_ + (row_increment_ * (height_ - i - 1)); - stream.write(reinterpret_cast(data_ptr),sizeof(unsigned char) * bytes_per_pixel_ * width_); + const unsigned char* data_ptr = &data_[(row_increment_ * (height_ - i - 1))]; + + stream.write(reinterpret_cast(data_ptr), sizeof(unsigned char) * bytes_per_pixel_ * width_); stream.write(padding_data,padding); } @@ -427,7 +466,7 @@ class bitmap_image { unsigned char mask = static_cast(~(1 << bitr_index)); - for (unsigned char* itr = data_; itr != data_ + length_; ++itr) + for (unsigned char* itr = data(); itr != end(); ++itr) { *itr &= mask; } @@ -437,7 +476,7 @@ class bitmap_image { unsigned char mask = static_cast(1 << bitr_index); - for (unsigned char* itr = data_; itr != data_ + length_; ++itr) + for (unsigned char* itr = data(); itr != end(); ++itr) { *itr |= mask; } @@ -445,7 +484,7 @@ class bitmap_image inline void set_all_ith_channels(const unsigned int& channel, const unsigned char& value) { - for (unsigned char* itr = (data_ + channel); itr < (data_ + length_); itr += bytes_per_pixel_) + for (unsigned char* itr = (data() + channel); itr < end(); itr += bytes_per_pixel_) { *itr = value; } @@ -453,7 +492,7 @@ class bitmap_image inline void set_channel(const color_plane color,const unsigned char& value) { - for (unsigned char* itr = (data_ + offset(color)); itr < (data_ + length_); itr += bytes_per_pixel_) + for (unsigned char* itr = (data() + offset(color)); itr < end(); itr += bytes_per_pixel_) { *itr = value; } @@ -461,7 +500,7 @@ class bitmap_image inline void ror_channel(const color_plane color, const unsigned int& ror) { - for (unsigned char* itr = (data_ + offset(color)); itr < (data_ + length_); itr += bytes_per_pixel_) + for (unsigned char* itr = (data() + offset(color)); itr < end(); itr += bytes_per_pixel_) { *itr = static_cast(((*itr) >> ror) | ((*itr) << (8 - ror))); } @@ -469,7 +508,7 @@ class bitmap_image inline void set_all_channels(const unsigned char& value) { - for (unsigned char* itr = data_; itr < (data_ + length_); ) + for (unsigned char* itr = data(); itr < end(); ) { *(itr++) = value; } @@ -479,7 +518,7 @@ class bitmap_image const unsigned char& g_value, const unsigned char& b_value) { - for (unsigned char* itr = (data_ + 0); itr < (data_ + length_); itr += bytes_per_pixel_) + for (unsigned char* itr = (data() + 0); itr < end(); itr += bytes_per_pixel_) { *(itr + 0) = b_value; *(itr + 1) = g_value; @@ -489,12 +528,15 @@ class bitmap_image inline void invert_color_planes() { - for (unsigned char* itr = data_; itr < (data_ + length_); *itr = ~(*itr), ++itr); + for (unsigned char* itr = data(); itr < end(); *itr = ~(*itr), ++itr); } - inline void add_to_color_plane(const color_plane color,const unsigned char& value) + inline void add_to_color_plane(const color_plane color, const unsigned char& value) { - for (unsigned char* itr = (data_ + offset(color)); itr < (data_ + length_); (*itr) += value, itr += bytes_per_pixel_); + for (unsigned char* itr = (data() + offset(color)); itr < end(); itr += bytes_per_pixel_) + { + (*itr) += value; + } } inline void convert_to_grayscale() @@ -505,25 +547,32 @@ class bitmap_image if (rgb_mode == channel_mode_) { - double tmp = r_scaler; - r_scaler = b_scaler; - b_scaler = tmp; + std::swap(r_scaler, b_scaler); } - for (unsigned char* itr = data_; itr < (data_ + length_); ) + for (unsigned char* itr = data(); itr < end(); ) { - unsigned char gray_value = static_cast((r_scaler * (*(itr + 2))) + - (g_scaler * (*(itr + 1))) + - (b_scaler * (*(itr + 0))) ); + unsigned char gray_value = static_cast + ( + (r_scaler * (*(itr + 2))) + + (g_scaler * (*(itr + 1))) + + (b_scaler * (*(itr + 0))) + ); + *(itr++) = gray_value; *(itr++) = gray_value; *(itr++) = gray_value; } } - inline const unsigned char* data() + inline const unsigned char* data() const { - return data_; + return data_.data(); + } + + inline unsigned char* data() + { + return const_cast(data_.data()); } inline void bgr_to_rgb() @@ -546,8 +595,8 @@ class bitmap_image inline void reverse() { - unsigned char* itr1 = data_; - unsigned char* itr2 = (data_ + length_) - bytes_per_pixel_; + unsigned char* itr1 = data(); + unsigned char* itr2 = end() - bytes_per_pixel_; while (itr1 < itr2) { @@ -555,9 +604,8 @@ class bitmap_image { unsigned char* citr1 = itr1 + i; unsigned char* citr2 = itr2 + i; - unsigned char tmp = *citr1; - *citr1 = *citr2; - *citr2 = tmp; + + std::swap(*citr1,*citr2); } itr1 += bytes_per_pixel_; @@ -578,9 +626,8 @@ class bitmap_image { unsigned char* p1 = (itr1 + i); unsigned char* p2 = (itr2 + i); - unsigned char tmp = *p1; - *p1 = *p2; - *p2 = tmp; + + std::swap(*p1,*p2); } itr1 += bytes_per_pixel_; @@ -598,16 +645,14 @@ class bitmap_image for (std::size_t x = 0; x < row_increment_; ++x) { - unsigned char tmp = *(itr1 + x); - *(itr1 + x) = *(itr2 + x); - *(itr2 + x) = tmp; + std::swap(*(itr1 + x),*(itr2 + x)); } } } inline void export_color_plane(const color_plane color, unsigned char* image) { - for (unsigned char* itr = (data_ + offset(color)); itr < (data_ + length_); ++image, itr += bytes_per_pixel_) + for (unsigned char* itr = (data() + offset(color)); itr < end(); ++image, itr += bytes_per_pixel_) { (*image) = (*itr); } @@ -625,13 +670,14 @@ class bitmap_image image.clear(); - unsigned char* itr1 = (data_ + offset(color)); - unsigned char* itr1_end = (data_ + length_); - unsigned char* itr2 = (image.data_ + offset(color)); + unsigned char* itr1 = (data() + offset(color)); + unsigned char* itr1_end = end(); + unsigned char* itr2 = (image.data() + offset(color)); while (itr1 < itr1_end) { (*itr2) = (*itr1); + itr1 += bytes_per_pixel_; itr2 += bytes_per_pixel_; } @@ -639,20 +685,28 @@ class bitmap_image inline void export_response_image(const color_plane color, double* response_image) { - for (unsigned char* itr = (data_ + offset(color)); itr < (data_ + length_); ++response_image, itr += bytes_per_pixel_) + double* resp_itr = response_image; + + for (unsigned char* itr = (data() + offset(color)); itr < end(); ++response_image, itr += bytes_per_pixel_) { - (*response_image) = (1.0 * (*itr)) / 256.0; + *(resp_itr++) = (1.0 * (*itr)) / 256.0; } } - inline void export_gray_scale_response_image(double* response_image) + inline void export_gray_scale_response_image(double* response_image) const { - for (unsigned char* itr = data_; itr < (data_ + length_); itr += bytes_per_pixel_) + double* resp_itr = response_image; + + for (const unsigned char* itr = data(); itr < end(); itr += bytes_per_pixel_) { - unsigned char gray_value = static_cast((0.299 * (*(itr + 2))) + - (0.587 * (*(itr + 1))) + - (0.114 * (*(itr + 0)))); - (*response_image) = (1.0 * gray_value) / 256.0; + unsigned char gray_value = static_cast + ( + (0.299 * (*(itr + 2))) + + (0.587 * (*(itr + 1))) + + (0.114 * (*(itr + 0))) + ); + + *(resp_itr++) = (1.0 * gray_value) / 256.0; } } @@ -661,11 +715,11 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { - (*blue) = (1.0 * (*(itr++))) / 256.0; + (*blue ) = (1.0 * (*(itr++))) / 256.0; (*green) = (1.0 * (*(itr++))) / 256.0; - (*red) = (1.0 * (*(itr++))) / 256.0; + (*red ) = (1.0 * (*(itr++))) / 256.0; } } @@ -674,11 +728,11 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { - (*blue) = (1.0f * (*(itr++))) / 256.0f; + (*blue ) = (1.0f * (*(itr++))) / 256.0f; (*green) = (1.0f * (*(itr++))) / 256.0f; - (*red) = (1.0f * (*(itr++))) / 256.0f; + (*red ) = (1.0f * (*(itr++))) / 256.0f; } } @@ -687,11 +741,11 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { - (*blue) = *(itr++); + (*blue ) = *(itr++); (*green) = *(itr++); - (*red) = *(itr++); + (*red ) = *(itr++); } } @@ -700,11 +754,11 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++y, ++cb, ++cr) + for (const unsigned char* itr = data(); itr < end(); ++y, ++cb, ++cr) { - double blue = (1.0 * (*(itr++))); - double green = (1.0 * (*(itr++))); - double red = (1.0 * (*(itr++))); + const double blue = (1.0 * (*(itr++))); + const double green = (1.0 * (*(itr++))); + const double red = (1.0 * (*(itr++))); ( *y) = clamp( 16.0 + (1.0/256.0) * ( 65.738 * red + 129.057 * green + 25.064 * blue),1.0,254); (*cb) = clamp(128.0 + (1.0/256.0) * (- 37.945 * red - 74.494 * green + 112.439 * blue),1.0,254); @@ -717,11 +771,11 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { - (*blue) = (1.0 * (*(itr++))); + (*blue ) = (1.0 * (*(itr++))); (*green) = (1.0 * (*(itr++))); - (*red) = (1.0 * (*(itr++))); + (*red ) = (1.0 * (*(itr++))); } } @@ -730,11 +784,11 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { - (*blue) = (1.0f * (*(itr++))); + (*blue ) = (1.0f * (*(itr++))); (*green) = (1.0f * (*(itr++))); - (*red) = (1.0f * (*(itr++))); + (*red ) = (1.0f * (*(itr++))); } } @@ -743,7 +797,7 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { *(itr++) = static_cast(256.0 * (*blue )); *(itr++) = static_cast(256.0 * (*green)); @@ -756,7 +810,7 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { *(itr++) = static_cast(256.0f * (*blue )); *(itr++) = static_cast(256.0f * (*green)); @@ -769,7 +823,7 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { *(itr++) = (*blue ); *(itr++) = (*green); @@ -782,7 +836,7 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++y, ++cb, ++cr) + for (unsigned char* itr = data(); itr < end(); ++y, ++cb, ++cr) { double y_ = (*y); double cb_ = (*cb); @@ -794,12 +848,29 @@ class bitmap_image } } + inline void import_gray_scale_clamped(double* gray) + { + if (bgr_mode != channel_mode_) + return; + + for (unsigned char* itr = data(); itr < end(); ++gray) + { + unsigned char c = static_cast(clamp(256.0 * (*gray),0.0,255.0)); + + *(itr + 0) = c; + *(itr + 1) = c; + *(itr + 2) = c; + + itr += 3; + } + } + inline void import_rgb_clamped(double* red, double* green, double* blue) { if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { *(itr++) = static_cast(clamp(256.0 * (*blue ),0.0,255.0)); *(itr++) = static_cast(clamp(256.0 * (*green),0.0,255.0)); @@ -812,7 +883,7 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { *(itr++) = static_cast(clamp(256.0f * (*blue ),0.0,255.0)); *(itr++) = static_cast(clamp(256.0f * (*green),0.0,255.0)); @@ -825,7 +896,7 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { *(itr++) = static_cast(*blue ); *(itr++) = static_cast(*green); @@ -838,7 +909,7 @@ class bitmap_image if (bgr_mode != channel_mode_) return; - for (unsigned char* itr = data_; itr < (data_ + length_); ++red, ++green, ++blue) + for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) { *(itr++) = static_cast(*blue ); *(itr++) = static_cast(*green); @@ -883,17 +954,17 @@ class bitmap_image const unsigned char* itr1[3]; const unsigned char* itr2[3]; - s_itr[0] = dest.data_ + 0; - s_itr[1] = dest.data_ + 1; - s_itr[2] = dest.data_ + 2; + s_itr[0] = dest.data() + 0; + s_itr[1] = dest.data() + 1; + s_itr[2] = dest.data() + 2; - itr1[0] = data_ + 0; - itr1[1] = data_ + 1; - itr1[2] = data_ + 2; + itr1[0] = data() + 0; + itr1[1] = data() + 1; + itr1[2] = data() + 2; - itr2[0] = data_ + row_increment_ + 0; - itr2[1] = data_ + row_increment_ + 1; - itr2[2] = data_ + row_increment_ + 2; + itr2[0] = data() + row_increment_ + 0; + itr2[1] = data() + row_increment_ + 1; + itr2[2] = data() + row_increment_ + 2; unsigned int total = 0; @@ -904,10 +975,15 @@ class bitmap_image for (unsigned int k = 0; k < bytes_per_pixel_; s_itr[k] += bytes_per_pixel_, ++k) { total = 0; - total += *(itr1[k]); itr1[k] += bytes_per_pixel_; - total += *(itr1[k]); itr1[k] += bytes_per_pixel_; - total += *(itr2[k]); itr2[k] += bytes_per_pixel_; - total += *(itr2[k]); itr2[k] += bytes_per_pixel_; + total += *(itr1[k]); + total += *(itr1[k]); + total += *(itr2[k]); + total += *(itr2[k]); + + itr1[k] += bytes_per_pixel_; + itr1[k] += bytes_per_pixel_; + itr2[k] += bytes_per_pixel_; + itr2[k] += bytes_per_pixel_; *(s_itr[k]) = static_cast(total >> 2); } @@ -918,18 +994,27 @@ class bitmap_image for (unsigned int k = 0; k < bytes_per_pixel_; s_itr[k] += bytes_per_pixel_, ++k) { total = 0; - total += *(itr1[k]); itr1[k] += bytes_per_pixel_; - total += *(itr2[k]); itr2[k] += bytes_per_pixel_; + total += *(itr1[k]); + total += *(itr2[k]); + + itr1[k] += bytes_per_pixel_; + itr2[k] += bytes_per_pixel_; *(s_itr[k]) = static_cast(total >> 1); } } - for (unsigned int k = 0; k < bytes_per_pixel_; itr1[k] += row_increment_, ++k); + for (unsigned int k = 0; k < bytes_per_pixel_; ++k) + { + itr1[k] += row_increment_; + } if (j != (vertical_upper - 1)) { - for (unsigned int k = 0; k < bytes_per_pixel_; itr2[k] += row_increment_, ++k); + for (unsigned int k = 0; k < bytes_per_pixel_; ++k) + { + itr2[k] += row_increment_; + } } } @@ -940,8 +1025,11 @@ class bitmap_image for (unsigned int k = 0; k < bytes_per_pixel_; s_itr[k] += bytes_per_pixel_, ++k) { total = 0; - total += *(itr1[k]); itr1[k] += bytes_per_pixel_; - total += *(itr2[k]); itr2[k] += bytes_per_pixel_; + total += *(itr1[k]); + total += *(itr2[k]); + + itr1[k] += bytes_per_pixel_; + itr2[k] += bytes_per_pixel_; *(s_itr[k]) = static_cast(total >> 1); } @@ -970,17 +1058,17 @@ class bitmap_image unsigned char* itr1[3]; unsigned char* itr2[3]; - s_itr[0] = data_ + 0; - s_itr[1] = data_ + 1; - s_itr[2] = data_ + 2; + s_itr[0] = data() + 0; + s_itr[1] = data() + 1; + s_itr[2] = data() + 2; - itr1[0] = dest.data_ + 0; - itr1[1] = dest.data_ + 1; - itr1[2] = dest.data_ + 2; + itr1[0] = dest.data() + 0; + itr1[1] = dest.data() + 1; + itr1[2] = dest.data() + 2; - itr2[0] = dest.data_ + dest.row_increment_ + 0; - itr2[1] = dest.data_ + dest.row_increment_ + 1; - itr2[2] = dest.data_ + dest.row_increment_ + 2; + itr2[0] = dest.data() + dest.row_increment_ + 0; + itr2[1] = dest.data() + dest.row_increment_ + 1; + itr2[2] = dest.data() + dest.row_increment_ + 2; for (unsigned int j = 0; j < height_; ++j) { @@ -1019,9 +1107,9 @@ class bitmap_image return; } - unsigned char* itr1 = data_; - unsigned char* itr1_end = data_ + length_; - unsigned char* itr2 = image.data_; + unsigned char* itr1 = data(); + unsigned char* itr1_end = end(); + const unsigned char* itr2 = image.data(); double alpha_compliment = 1.0 - alpha; @@ -1043,14 +1131,14 @@ class bitmap_image return 0.0; } - unsigned char* itr1 = data_; - unsigned char* itr2 = image.data_; + unsigned char* itr1 = data(); + const unsigned char* itr2 = image.data(); double mse = 0.0; - while (itr1 != (data_ + length_)) + while (itr1 != end()) { - double v = (static_cast(*itr1) - static_cast(*itr2)); + const double v = (static_cast(*itr1) - static_cast(*itr2)); mse += v * v; ++itr1; @@ -1064,6 +1152,7 @@ class bitmap_image else { mse /= (3.0 * width_ * height_); + return 20.0 * std::log10(255.0 / std::sqrt(mse)); } } @@ -1072,7 +1161,7 @@ class bitmap_image const unsigned int& y, const bitmap_image& image) { - if ((x + image.width()) > width_) { return 0.0; } + if ((x + image.width() ) > width_ ) { return 0.0; } if ((y + image.height()) > height_) { return 0.0; } double mse = 0.0; @@ -1110,7 +1199,7 @@ class bitmap_image { std::fill(hist,hist + 256,0.0); - for (unsigned char* itr = (data_ + offset(color)); itr < (data_ + length_); itr += bytes_per_pixel_) + for (unsigned char* itr = (data() + offset(color)); itr < end(); itr += bytes_per_pixel_) { ++hist[(*itr)]; } @@ -1162,7 +1251,7 @@ class bitmap_image { unsigned char current_color = 0; - for (unsigned char* itr = data_; itr < (data_ + length_);) + for (unsigned char* itr = data(); itr < end();) { (*itr++) = (current_color); (*itr++) = (current_color); @@ -1172,8 +1261,29 @@ class bitmap_image } } + inline void reverse_channels() + { + if (3 != bytes_per_pixel_) + return; + + for (unsigned char* itr = data(); itr < end(); itr += bytes_per_pixel_) + { + std::swap(*(itr + 0),*(itr + 2)); + } + } + private: + inline const unsigned char* end() const + { + return data_.data() + data_.size(); + } + + inline unsigned char* end() + { + return const_cast(data() + data_.size()); + } + struct bitmap_file_header { unsigned short type; @@ -1182,13 +1292,18 @@ class bitmap_image unsigned short reserved2; unsigned int off_bits; - unsigned int struct_size() + unsigned int struct_size() const { - return sizeof(type) + - sizeof(size) + + return sizeof(type ) + + sizeof(size ) + sizeof(reserved1) + sizeof(reserved2) + - sizeof(off_bits); + sizeof(off_bits ) ; + } + + void clear() + { + std::memset(this,0x00,sizeof(bitmap_file_header)); } }; @@ -1206,73 +1321,80 @@ class bitmap_image unsigned int clr_used; unsigned int clr_important; - unsigned int struct_size() + unsigned int struct_size() const { - return sizeof(size) + - sizeof(width) + - sizeof(height) + - sizeof(planes) + - sizeof(bit_count) + - sizeof(compression) + - sizeof(size_image) + + return sizeof(size ) + + sizeof(width ) + + sizeof(height ) + + sizeof(planes ) + + sizeof(bit_count ) + + sizeof(compression ) + + sizeof(size_image ) + sizeof(x_pels_per_meter) + sizeof(y_pels_per_meter) + - sizeof(clr_used) + - sizeof(clr_important); + sizeof(clr_used ) + + sizeof(clr_important ) ; + } + + void clear() + { + std::memset(this,0x00,sizeof(bitmap_information_header)); } }; - inline bool big_endian() + inline bool big_endian() const { unsigned int v = 0x01; return (1 != reinterpret_cast(&v)[0]); } - inline unsigned short flip(const unsigned short& v) + inline unsigned short flip(const unsigned short& v) const { return ((v >> 8) | (v << 8)); } - inline unsigned int flip(const unsigned int& v) + inline unsigned int flip(const unsigned int& v) const { - return (((v & 0xFF000000) >> 0x18) | - ((v & 0x000000FF) << 0x18) | - ((v & 0x00FF0000) >> 0x08) | - ((v & 0x0000FF00) << 0x08)); + return ( + ((v & 0xFF000000) >> 0x18) | + ((v & 0x000000FF) << 0x18) | + ((v & 0x00FF0000) >> 0x08) | + ((v & 0x0000FF00) << 0x08) + ); } - template + template inline void read_from_stream(std::ifstream& stream,T& t) { stream.read(reinterpret_cast(&t),sizeof(T)); } - template - inline void write_to_stream(std::ofstream& stream,const T& t) + template + inline void write_to_stream(std::ofstream& stream,const T& t) const { stream.write(reinterpret_cast(&t),sizeof(T)); } inline void read_bfh(std::ifstream& stream, bitmap_file_header& bfh) { - read_from_stream(stream,bfh.type); - read_from_stream(stream,bfh.size); + read_from_stream(stream,bfh.type ); + read_from_stream(stream,bfh.size ); read_from_stream(stream,bfh.reserved1); read_from_stream(stream,bfh.reserved2); - read_from_stream(stream,bfh.off_bits); + read_from_stream(stream,bfh.off_bits ); if (big_endian()) { - bfh.type = flip(bfh.type); - bfh.size = flip(bfh.size); + bfh.type = flip(bfh.type ); + bfh.size = flip(bfh.size ); bfh.reserved1 = flip(bfh.reserved1); bfh.reserved2 = flip(bfh.reserved2); - bfh.off_bits = flip(bfh.off_bits); + bfh.off_bits = flip(bfh.off_bits ); } } - inline void write_bfh(std::ofstream& stream, const bitmap_file_header& bfh) + inline void write_bfh(std::ofstream& stream, const bitmap_file_header& bfh) const { if (big_endian()) { @@ -1294,77 +1416,78 @@ class bitmap_image inline void read_bih(std::ifstream& stream,bitmap_information_header& bih) { - read_from_stream(stream,bih.size ); - read_from_stream(stream,bih.width ); - read_from_stream(stream,bih.height); - read_from_stream(stream,bih.planes); - read_from_stream(stream,bih.bit_count); - read_from_stream(stream,bih.compression); - read_from_stream(stream,bih.size_image); + read_from_stream(stream,bih.size ); + read_from_stream(stream,bih.width ); + read_from_stream(stream,bih.height ); + read_from_stream(stream,bih.planes ); + read_from_stream(stream,bih.bit_count ); + read_from_stream(stream,bih.compression ); + read_from_stream(stream,bih.size_image ); read_from_stream(stream,bih.x_pels_per_meter); read_from_stream(stream,bih.y_pels_per_meter); - read_from_stream(stream,bih.clr_used); - read_from_stream(stream,bih.clr_important); + read_from_stream(stream,bih.clr_used ); + read_from_stream(stream,bih.clr_important ); if (big_endian()) { - bih.size = flip(bih.size ); - bih.width = flip(bih.width ); - bih.height = flip(bih.height ); - bih.planes = flip(bih.planes ); - bih.bit_count = flip(bih.bit_count); - bih.compression = flip(bih.compression); - bih.size_image = flip(bih.size_image); + bih.size = flip(bih.size ); + bih.width = flip(bih.width ); + bih.height = flip(bih.height ); + bih.planes = flip(bih.planes ); + bih.bit_count = flip(bih.bit_count ); + bih.compression = flip(bih.compression ); + bih.size_image = flip(bih.size_image ); bih.x_pels_per_meter = flip(bih.x_pels_per_meter); bih.y_pels_per_meter = flip(bih.y_pels_per_meter); - bih.clr_used = flip(bih.clr_used); - bih.clr_important = flip(bih.clr_important); + bih.clr_used = flip(bih.clr_used ); + bih.clr_important = flip(bih.clr_important ); } } - inline void write_bih(std::ofstream& stream, const bitmap_information_header& bih) + inline void write_bih(std::ofstream& stream, const bitmap_information_header& bih) const { if (big_endian()) { - write_to_stream(stream,flip(bih.size)); - write_to_stream(stream,flip(bih.width)); - write_to_stream(stream,flip(bih.height)); - write_to_stream(stream,flip(bih.planes)); - write_to_stream(stream,flip(bih.bit_count)); - write_to_stream(stream,flip(bih.compression)); - write_to_stream(stream,flip(bih.size_image)); + write_to_stream(stream,flip(bih.size )); + write_to_stream(stream,flip(bih.width )); + write_to_stream(stream,flip(bih.height )); + write_to_stream(stream,flip(bih.planes )); + write_to_stream(stream,flip(bih.bit_count )); + write_to_stream(stream,flip(bih.compression )); + write_to_stream(stream,flip(bih.size_image )); write_to_stream(stream,flip(bih.x_pels_per_meter)); write_to_stream(stream,flip(bih.y_pels_per_meter)); - write_to_stream(stream,flip(bih.clr_used)); - write_to_stream(stream,flip(bih.clr_important)); + write_to_stream(stream,flip(bih.clr_used )); + write_to_stream(stream,flip(bih.clr_important )); } else { - write_to_stream(stream,bih.size); - write_to_stream(stream,bih.width); - write_to_stream(stream,bih.height); - write_to_stream(stream,bih.planes); - write_to_stream(stream,bih.bit_count); - write_to_stream(stream,bih.compression); - write_to_stream(stream,bih.size_image); + write_to_stream(stream,bih.size ); + write_to_stream(stream,bih.width ); + write_to_stream(stream,bih.height ); + write_to_stream(stream,bih.planes ); + write_to_stream(stream,bih.bit_count ); + write_to_stream(stream,bih.compression ); + write_to_stream(stream,bih.size_image ); write_to_stream(stream,bih.x_pels_per_meter); write_to_stream(stream,bih.y_pels_per_meter); - write_to_stream(stream,bih.clr_used); - write_to_stream(stream,bih.clr_important); + write_to_stream(stream,bih.clr_used ); + write_to_stream(stream,bih.clr_important ); } } + inline std::size_t file_size(const std::string& file_name) + { + std::ifstream file(file_name.c_str(),std::ios::in | std::ios::binary); + if (!file) return 0; + file.seekg (0, std::ios::end); + return static_cast(file.tellg()); + } + void create_bitmap() { - length_ = width_ * height_ * bytes_per_pixel_; row_increment_ = width_ * bytes_per_pixel_; - - if (0 != data_) - { - delete[] data_; - } - - data_ = new unsigned char[length_]; + data_.resize(height_ * row_increment_); } void load_bitmap() @@ -1377,89 +1500,199 @@ class bitmap_image return; } + width_ = 0; + height_ = 0; + bitmap_file_header bfh; bitmap_information_header bih; + bfh.clear(); + bih.clear(); + read_bfh(stream,bfh); read_bih(stream,bih); if (bfh.type != 19778) { + bfh.clear(); + bih.clear(); + stream.close(); + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - Invalid type value " << bfh.type << " expected 19778." << std::endl; return; } if (bih.bit_count != 24) { + bfh.clear(); + bih.clear(); + stream.close(); + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - Invalid bit depth " << bih.bit_count << " expected 24." << std::endl; return; } - height_ = bih.height; + if (bih.size != bih.struct_size()) + { + bfh.clear(); + bih.clear(); + + stream.close(); + + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - Invalid BIH size " << bih.size << " expected " << bih.struct_size() << std::endl; + + return; + } + width_ = bih.width; + height_ = bih.height; bytes_per_pixel_ = bih.bit_count >> 3; unsigned int padding = (4 - ((3 * width_) % 4)) % 4; char padding_data[4] = {0,0,0,0}; - create_bitmap(); + std::size_t bitmap_file_size = file_size(file_name_); - for (unsigned int i = 0; i < height_; ++i) + std::size_t bitmap_logical_size = (height_ * width_ * bytes_per_pixel_) + + (height_ * padding) + + bih.struct_size() + + bfh.struct_size() ; + + if (bitmap_file_size != bitmap_logical_size) { - unsigned char* data_ptr = row(height_ - i - 1); // read in inverted row order + bfh.clear(); + bih.clear(); - stream.read(reinterpret_cast(data_ptr),sizeof(char) * bytes_per_pixel_ * width_); - stream.read(padding_data,padding); - } - } + stream.close(); + + std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - Mismatch between logical and physical sizes of bitmap. " << + "Logical: " << bitmap_logical_size << " " << + "Physical: " << bitmap_file_size << std::endl; - inline void reverse_channels() - { - if (3 != bytes_per_pixel_) return; + } + + create_bitmap(); - for (unsigned char* itr = data_; itr < (data_ + length_); itr += bytes_per_pixel_) + for (unsigned int i = 0; i < height_; ++i) { - unsigned char tmp = *(itr + 0); + unsigned char* data_ptr = row(height_ - i - 1); // read in inverted row order - *(itr + 0) = *(itr + 2); - *(itr + 2) = tmp; + stream.read(reinterpret_cast(data_ptr), sizeof(char) * bytes_per_pixel_ * width_); + stream.read(padding_data,padding); } } - template + template inline T clamp(const T& v, const T& lower_range, const T& upper_range) { if (v < lower_range) - return lower_range; + return lower_range; else if (v > upper_range) return upper_range; else return v; } - std::string file_name_; - unsigned char* data_; - unsigned int length_; - unsigned int width_; - unsigned int height_; - unsigned int row_increment_; - unsigned int bytes_per_pixel_; - channel_mode channel_mode_; + std::string file_name_; + unsigned int width_; + unsigned int height_; + unsigned int row_increment_; + unsigned int bytes_per_pixel_; + channel_mode channel_mode_; + std::vector data_; }; - -struct rgb_store +struct rgb_t { - unsigned char red; + unsigned char red; unsigned char green; - unsigned char blue; + unsigned char blue; }; +inline bool operator==(const rgb_t& c0, const rgb_t& c1) +{ + return (c0.red == c1 .red) && + (c0.green == c1.green) && + (c0.blue == c1 .blue) ; +} + +inline bool operator!=(const rgb_t& c0, const rgb_t& c1) +{ + return (c0.red != c1 .red) || + (c0.green != c1.green) || + (c0.blue != c1 .blue) ; +} + +inline std::size_t hamming_distance(const rgb_t& c0, const rgb_t& c1) +{ + std::size_t result = 0; + + if (c0.red != c1 .red) ++result; + if (c0.green != c1.green) ++result; + if (c0.blue != c1 .blue) ++result; + + return result; +} + +inline rgb_t make_colour(const unsigned int& red, const unsigned int& green, const unsigned int& blue) +{ + rgb_t result; + + result.red = static_cast(red ); + result.green = static_cast(green); + result.blue = static_cast(blue ); + + return result; +} + +template +inline void generate_colours(const std::size_t& steps, const rgb_t c0, const rgb_t& c1, OutputIterator out) +{ + double dr = ((double)c1.red - (double)c0.red ) / steps; + double dg = ((double)c1.green - (double)c0.green ) / steps; + double db = ((double)c1.blue - (double)c0.blue ) / steps; + + for (std::size_t i = 0; i < steps; ++i) + { + rgb_t c; + + c.red = static_cast(c0.red + (i * dr)); + c.green = static_cast(c0.green + (i * dg)); + c.blue = static_cast(c0.blue + (i * db)); + + *(out++) = c; + } +} + +template +inline std::size_t convert_rsp_to_image(const ResponseImage& resp_image, const Palette& palette, bitmap_image& image) +{ + if ( + (resp_image.width () > image.width ()) || + (resp_image.height() > image.height()) + ) + return 0; + + for (std::size_t y = 0; y < resp_image.height(); ++y) + { + for (std::size_t x = 0; x < resp_image.width(); ++x) + { + const double v = resp_image(x,y); + + unsigned int index = static_cast((v < 0) ? 0 : v > (palette.size()) ? (palette.size() - 1) : v); + + image.set_pixel(x,y,palette[index]); + } + } + + return (resp_image.width() * resp_image.height()); +} + inline void rgb_to_ycbcr(const unsigned int& length, double* red, double* green, double* blue, double* y, double* cb, double* cr) { @@ -1503,7 +1736,7 @@ inline void subsample(const unsigned int& width, const double* source, unsigned int& w, unsigned int& h, - double** dest) + double*& dest) { /* Single channel. */ @@ -1532,11 +1765,11 @@ inline void subsample(const unsigned int& width, unsigned int horizontal_upper = (odd_width) ? w - 1 : w; unsigned int vertical_upper = (odd_height) ? h - 1 : h; - *dest = new double[w * h]; + dest = new double[w * h]; - double* s_itr = *dest; - const double* itr1 = source; - const double* itr2 = source + width; + double* s_itr = dest; + const double* itr1 = source; + const double* itr2 = source + width; for (unsigned int j = 0; j < vertical_upper; ++j) { @@ -1551,7 +1784,7 @@ inline void subsample(const unsigned int& width, if (odd_width) { - (*(s_itr++)) = ( (*itr1++) + (*itr2++) ) / 2.0; + (*(s_itr++)) = ((*itr1++) + (*itr2++)) / 2.0; } itr1 += width; @@ -1583,18 +1816,18 @@ inline void upsample(const unsigned int& width, const double* source, unsigned int& w, unsigned int& h, - double** dest) + double*& dest) { /* Single channel. */ w = 2 * width; h = 2 * height; - *dest = new double[w * h]; + dest = new double[w * h]; const double* s_itr = source; - double* itr1 = *dest; - double* itr2 = *dest + w; + double* itr1 = dest; + double* itr2 = dest + w; for (unsigned int j = 0; j < height; ++j) { @@ -1708,23 +1941,23 @@ inline void plasma(bitmap_image& image, const double& width, const double& height, const double& c1, const double& c2, const double& c3, const double& c4, - const double& roughness = 3.0, - const rgb_store colormap[] = 0) + const double& roughness = 3.0, + const rgb_t colormap[] = 0) { // Note: c1,c2,c3,c4 -> [0.0,1.0] - double half_width = ( width / 2.0); - double half_height = (height / 2.0); + const double half_width = ( width / 2.0); + const double half_height = (height / 2.0); if ((width >= 1.0) || (height >= 1.0)) { - double corner1 = (c1 + c2) / 2.0; - double corner2 = (c2 + c3) / 2.0; - double corner3 = (c3 + c4) / 2.0; - double corner4 = (c4 + c1) / 2.0; - double center = (c1 + c2 + c3 + c4) / 4.0 + - ((1.0 * ::rand() /(1.0 * RAND_MAX)) - 0.5) * // should use a better rng - ((1.0 * half_width + half_height) / (image.width() + image.height()) * roughness); + const double corner1 = (c1 + c2) / 2.0; + const double corner2 = (c2 + c3) / 2.0; + const double corner3 = (c3 + c4) / 2.0; + const double corner4 = (c4 + c1) / 2.0; + double center = (c1 + c2 + c3 + c4) / 4.0 + + ((1.0 * ::rand() /(1.0 * RAND_MAX)) - 0.5) * // should use a better rng + ((1.0 * half_width + half_height) / (image.width() + image.height()) * roughness); center = std::min(std::max(0.0,center),1.0); @@ -1735,13 +1968,28 @@ inline void plasma(bitmap_image& image, } else { - rgb_store color = colormap[static_cast(1000.0 * ((c1 + c2 + c3 + c4) / 4.0)) % 1000]; - image.set_pixel(static_cast(x),static_cast(y),color.red,color.green,color.blue); + rgb_t color = colormap[static_cast(1000.0 * ((c1 + c2 + c3 + c4) / 4.0)) % 1000]; + + image.set_pixel(static_cast(x),static_cast(y),color); } } -inline double psnr_region(const unsigned int& x, const unsigned int& y, - const unsigned int& width, const unsigned int& height, +inline void plasma(bitmap_image& image, + const double& c1, const double& c2, + const double& c3, const double& c4, + const double& roughness = 3.0, + const rgb_t colormap[] = 0) +{ + plasma + ( + image, 0, 0, image.width(), image.height(), + c1, c2, c3, c4, + roughness, colormap + ); +} + +inline double psnr_region(const unsigned int& x, const unsigned int& y, + const unsigned int& width, const unsigned int& height, const bitmap_image& image1, const bitmap_image& image2) { if ( @@ -1752,7 +2000,7 @@ inline double psnr_region(const unsigned int& x, const unsigned int& y, return 0.0; } - if ((x + width) > image1.width()) { return 0.0; } + if ((x + width ) > image1.width() ) { return 0.0; } if ((y + height) > image1.height()) { return 0.0; } double mse = 0.0; @@ -1788,39 +2036,46 @@ inline void hierarchical_psnr_r(const double& x, const double& y, const bitmap_image& image1, bitmap_image& image2, const double& threshold, - const rgb_store colormap[]) + const rgb_t colormap[]) { if ((width <= 4.0) || (height <= 4.0)) { - double psnr = psnr_region(static_cast(x), - static_cast(y), - static_cast(width), - static_cast(height), - image1,image2); + const double psnr = psnr_region + ( + static_cast(x), + static_cast(y), + static_cast(width), + static_cast(height), + image1, image2 + ); if (psnr < threshold) { - rgb_store c = colormap[static_cast(1000.0 * (1.0 - (psnr / threshold)))]; - image2.set_region(static_cast(x), - static_cast(y), - static_cast(width + 1), - static_cast(height + 1), - c.red,c.green,c.blue); + rgb_t c = colormap[static_cast(1000.0 * (1.0 - (psnr / threshold)))]; + + image2.set_region + ( + static_cast(x), + static_cast(y), + static_cast(width + 1), + static_cast(height + 1), + c.red, c.green, c.blue + ); } } else { - double half_width = ( width / 2.0); - double half_height = (height / 2.0); + const double half_width = ( width / 2.0); + const double half_height = (height / 2.0); - hierarchical_psnr_r(x , y , half_width, half_height,image1,image2,threshold,colormap); - hierarchical_psnr_r(x + half_width, y , half_width, half_height,image1,image2,threshold,colormap); - hierarchical_psnr_r(x + half_width, y + half_height, half_width, half_height,image1,image2,threshold,colormap); - hierarchical_psnr_r(x , y + half_height, half_width, half_height,image1,image2,threshold,colormap); + hierarchical_psnr_r(x , y , half_width, half_height, image1, image2, threshold, colormap); + hierarchical_psnr_r(x + half_width, y , half_width, half_height, image1, image2, threshold, colormap); + hierarchical_psnr_r(x + half_width, y + half_height, half_width, half_height, image1, image2, threshold, colormap); + hierarchical_psnr_r(x , y + half_height, half_width, half_height, image1, image2, threshold, colormap); } } -inline void hierarchical_psnr(bitmap_image& image1,bitmap_image& image2, const double threshold, const rgb_store colormap[]) +inline void hierarchical_psnr(bitmap_image& image1, bitmap_image& image2, const double threshold, const rgb_t colormap[]) { if ( (image1.width() != image2.width ()) || @@ -1830,11 +2085,21 @@ inline void hierarchical_psnr(bitmap_image& image1,bitmap_image& image2, const d return; } - double psnr = psnr_region(0,0,image1.width(),image1.height(),image1,image2); + const double psnr = psnr_region + ( + 0, 0, image1.width(), image1.height(), + image1, image2 + ); if (psnr < threshold) { - hierarchical_psnr_r(0,0, image1.width(), image1.height(),image1,image2,threshold,colormap); + hierarchical_psnr_r + ( + 0, 0, image1.width(), image1.height(), + image1, image2, + threshold, + colormap + ); } } @@ -1883,9 +2148,10 @@ class image_drawer if (dy > dx) { - steep = x1; x1 = y1; y1 = steep; /* swap x1 and y1 */ - steep = dx; dx = dy; dy = steep; /* swap dx and dy */ - steep = sx; sx = sy; sy = steep; /* swap sx and sy */ + std::swap(x1,y1); + std::swap(dx,dy); + std::swap(sx,sy); + steep = 1; } @@ -1961,10 +2227,10 @@ class image_drawer while (d2 < 0) { - plot_pen_pixel(positive_tx,positive_ty); - plot_pen_pixel(positive_tx,negative_ty); - plot_pen_pixel(negative_tx,positive_ty); - plot_pen_pixel(negative_tx,negative_ty); + plot_pen_pixel(positive_tx, positive_ty); + plot_pen_pixel(positive_tx, negative_ty); + plot_pen_pixel(negative_tx, positive_ty); + plot_pen_pixel(negative_tx, negative_ty); ++y; @@ -1991,10 +2257,10 @@ class image_drawer do { - plot_pen_pixel(positive_tx,positive_ty); - plot_pen_pixel(positive_tx,negative_ty); - plot_pen_pixel(negative_tx,positive_ty); - plot_pen_pixel(negative_tx,negative_ty); + plot_pen_pixel(positive_tx, positive_ty); + plot_pen_pixel(positive_tx, negative_ty); + plot_pen_pixel(negative_tx, positive_ty); + plot_pen_pixel(negative_tx, negative_ty); x--; t8 = t8 - t6; @@ -2023,10 +2289,10 @@ class image_drawer while (radius >= 0) { - plot_pen_pixel(centerx + x,centery + radius); - plot_pen_pixel(centerx + x,centery - radius); - plot_pen_pixel(centerx - x,centery + radius); - plot_pen_pixel(centerx - x,centery - radius); + plot_pen_pixel(centerx + x, centery + radius); + plot_pen_pixel(centerx + x, centery - radius); + plot_pen_pixel(centerx - x, centery + radius); + plot_pen_pixel(centerx - x, centery - radius); if ((d + radius) > 0) d -= ((--radius) << 1) - 1; @@ -2072,6 +2338,14 @@ class image_drawer void plot_pixel(int x, int y) { + if ( + (x < 0) || + (y < 0) || + (x >= static_cast(image_.width ())) || + (y >= static_cast(image_.height())) + ) + return; + image_.set_pixel(x,y,pen_color_red_,pen_color_green_,pen_color_blue_); } @@ -2092,6 +2366,14 @@ class image_drawer pen_color_blue_ = blue; } + template + void pen_color(const RGB colour) + { + pen_color_red_ = colour.red; + pen_color_green_ = colour.green; + pen_color_blue_ = colour.blue; + } + private: image_drawer(const image_drawer& id); @@ -2104,7 +2386,775 @@ class image_drawer unsigned char pen_color_blue_; }; -const rgb_store autumn_colormap[1000] = { +class cartesian_canvas +{ +public: + + cartesian_canvas(const double x_length, const double y_length) + : width_div2_ (0.0), + height_div2_(0.0), + min_x_ (0.0), + min_y_ (0.0), + max_x_ (0.0), + max_y_ (0.0), + draw_ (image_) + { + setup_canvas(x_length,y_length); + } + + inline bool operator!() + { + return !image_; + } + + void rectangle(double x1, double y1, double x2, double y2) + { + line_segment(x1, y1, x2, y1); + line_segment(x2, y1, x2, y2); + line_segment(x2, y2, x1, y2); + line_segment(x1, y2, x1, y1); + } + + void triangle(double x1, double y1, double x2, double y2, double x3, double y3) + { + line_segment(x1, y1, x2, y2); + line_segment(x2, y2, x3, y3); + line_segment(x3, y3, x1, y1); + } + + void quadix(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) + { + line_segment(x1, y1, x2, y2); + line_segment(x2, y2, x3, y3); + line_segment(x3, y3, x4, y4); + line_segment(x4, y4, x1, y1); + } + + void line_segment(double x1, double y1, double x2, double y2) + { + if (clip(x1, y1, x2, y2)) + { + const int sc_x1 = static_cast(cart_to_screen_x(x1)); + const int sc_x2 = static_cast(cart_to_screen_x(x2)); + const int sc_y1 = static_cast(cart_to_screen_y(y1)); + const int sc_y2 = static_cast(cart_to_screen_y(y2)); + + draw_.line_segment(sc_x1, sc_y1, sc_x2, sc_y2); + } + } + + void horiztonal_line_segment(double x1, double x2, double y) + { + x1 = clamp_x(x1); + x2 = clamp_x(x2); + y = clamp_y( y); + + const int sc_x1 = static_cast(cart_to_screen_x(x1)); + const int sc_x2 = static_cast(cart_to_screen_x(x2)); + const int sc_y = static_cast(cart_to_screen_y(y )); + + draw_.horiztonal_line_segment(sc_x1, sc_x2, sc_y); + } + + void vertical_line_segment(double y1, double y2, double x) + { + y1 = clamp_y(y1); + y2 = clamp_y(y2); + x = clamp_x( x); + + const int sc_y1 = static_cast(cart_to_screen_y(y1)); + const int sc_y2 = static_cast(cart_to_screen_y(y2)); + const int sc_x = static_cast(cart_to_screen_x(x )); + + draw_.vertical_line_segment(sc_y1, sc_y2, sc_x); + } + + void ellipse(double centerx, double centery, double a, double b) + { + + const int sc_cx = static_cast(cart_to_screen_x(centerx)); + const int sc_cy = static_cast(cart_to_screen_y(centery)); + + draw_.ellipse(sc_cx, sc_cy, static_cast(a), static_cast(b)); + } + + void circle(double centerx, double centery, double radius) + { + const int sc_cx = static_cast(cart_to_screen_x(centerx)); + const int sc_cy = static_cast(cart_to_screen_y(centery)); + + draw_.circle(sc_cx, sc_cy, static_cast(radius)); + } + + void fill_rectangle(double x1, double y1, double x2, double y2) + { + if (y1 > y2) + std::swap(y1, y2); + + for (double y = y1; y <= y2; y += 0.5) + { + line_segment(x1, y, x2, y); + } + } + + void fill_triangle(double x1, double y1, double x2, double y2, double x3, double y3) + { + typedef std::pair point_t; + + std::vector p; + + p.push_back(std::make_pair(x1,y1)); + p.push_back(std::make_pair(x2,y2)); + p.push_back(std::make_pair(x3,y3)); + + if (p[0].second > p[1].second) + std::swap(p[0],p[1]); + if (p[0].second > p[2].second) + std::swap(p[0],p[2]); + if (p[1].second > p[2].second) + std::swap(p[1],p[2]); + + class draw_modes + { + private: + + cartesian_canvas& canvas; + + // Needed for incompetent and broken msvc compiler versions + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4822) + #endif + draw_modes& operator=(const draw_modes&); + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + public: + + draw_modes(cartesian_canvas& c) + : canvas(c) + {} + + void bottom(const point_t& p0, const point_t& p1, const point_t& p2) + { + double m0 = (p1.first - p0.first) / (2.0 * (p1.second - p0.second)); + double m1 = (p2.first - p0.first) / (2.0 * (p2.second - p0.second)); + + double x0 = p0.first; + double x1 = p0.first; + + for (double y = p0.second; y <= p1.second; y += 0.5) + { + canvas.horiztonal_line_segment(x0, x1, y); + + x0 += m0; + x1 += m1; + } + } + + void top(const point_t& p0, const point_t& p1, const point_t& p2) + { + double m0 = (p2.first - p0.first) / (2.0 * (p2.second - p0.second)); + double m1 = (p2.first - p1.first) / (2.0 * (p2.second - p1.second)); + + double x0 = p2.first; + double x1 = p2.first; + + for (double y = p2.second; y >= p0.second; y -= 0.5) + { + canvas.horiztonal_line_segment(x0, x1, y); + + x0 -= m0; + x1 -= m1; + } + } + }; + + draw_modes dm(*this); + + const double eps = 0.00001; + + if (std::abs(p[1].second - p[2].second) < eps) + dm.bottom(p[0], p[1], p[2]); + else if (std::abs(p[0].second - p[1].second) < eps) + dm.top(p[0], p[1], p[2]); + else + { + point_t p3; + + p3.first = (p[0].first + ((p[1].second - p[0].second) / (p[2].second - p[0].second)) * (p[2].first - p[0].first)); + p3.second = p[1].second; + + dm.bottom(p[0], p[1], p3 ); + dm.top (p[1], p3 , p[2]); + } + } + + void fill_quadix(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) + { + fill_triangle(x1, y1, x2, y2, x3, y3); + fill_triangle(x1, y1, x3, y3, x4, y4); + } + + void fill_circle(double cx, double cy, double radius) + { + const double delta = 1.0; + double x = radius; + double y = 0.0; + double dx = delta - (2.0 * delta * radius); + double dy = 0.0; + double dr = 0.0; + + while (x >= y) + { + for (double i = cx - x; i <= cx + x; i += delta) + { + horiztonal_line_segment(cx - x, cx + x, cy + y); + horiztonal_line_segment(cx - x, cx + x, cy - y); + } + + for (double i = cx - y; i <= cx + y; i += delta) + { + horiztonal_line_segment(cx - y, cx + y, cy + x); + horiztonal_line_segment(cx - y, cx + y, cy - x); + } + + y += delta; + + dr += dy; + dy += 2.0 * delta; + + if ((2.0 * delta * dr + dx) > 0) + { + x -= delta; + dr += dx; + dx += 2.0 * delta; + } + } + } + + void plot_pen_pixel(double x, double y) + { + if ((x < min_x_) || (x > max_x_)) return; + if ((y < min_y_) || (y > max_y_)) return; + + const int sc_x = static_cast(cart_to_screen_x(x)); + const int sc_y = static_cast(cart_to_screen_y(y)); + + draw_.plot_pen_pixel(sc_x, sc_y); + } + + void plot_pixel(double x, double y) + { + if ((x < min_x_) || (x > max_x_)) return; + if ((y < min_y_) || (y > max_y_)) return; + + const int sc_x = static_cast(cart_to_screen_x(x)); + const int sc_y = static_cast(cart_to_screen_y(y)); + + draw_.plot_pixel(sc_x, sc_y); + } + + void pen_width(const unsigned int& width) + { + draw_.pen_width(width); + } + + void pen_color(const unsigned char& red, + const unsigned char& green, + const unsigned char& blue) + { + draw_.pen_color(red,green,blue); + } + + template + void pen_color(const RGB colour) + { + draw_.pen_color(colour); + } + + const bitmap_image& image() const + { + return image_; + } + + bitmap_image& image() + { + return image_; + } + + void set_widthheight(const double x_length, const double y_length) + { + setup_canvas(x_length, y_length); + } + + double min_x() const { return min_x_; } + double min_y() const { return min_y_; } + double max_x() const { return max_x_; } + double max_y() const { return max_y_; } + +private: + + void setup_canvas(const double x_length, const double y_length) + { + if ((x_length < 2.0) || (y_length < 2.0)) + return; + + width_div2_ = x_length / 2.0; + height_div2_ = y_length / 2.0; + + min_x_ = -width_div2_ ; + min_y_ = -height_div2_; + max_x_ = width_div2_ ; + max_y_ = height_div2_; + + image_.setwidth_height(static_cast(x_length) + 1, static_cast(y_length) + 1); + + image_.clear(0xFF); + } + + double clamp_x(const double& x) + { + if (x < min_x_) return min_x_; + else if (x > max_x_) return max_x_; + else return x; + } + + double clamp_y(const double& y) + { + if (y < min_y_) return min_y_; + else if (y > max_y_) return max_y_; + else return y; + } + + double cart_to_screen_x(const double& x) + { + return x + width_div2_; + } + + double cart_to_screen_y(const double& y) + { + return height_div2_ - y; + } + + enum clip_code + { + e_clip_bottom = 1, + e_clip_top = 2, + e_clip_left = 4, + e_clip_right = 8 + }; + + int out_code( + const double& x, const double& y, + const double& x1, const double& y1, + const double& x2, const double& y2 + ) + { + int result = 0; + if (y < y1) result |= e_clip_bottom; + else if (y > y2) result |= e_clip_top; + + if (x < x1) result |= e_clip_left; + else if (x > x2) result |= e_clip_right; + + return result; + } + + bool clip(double& x1, double& y1, double& x2, double& y2) + { + bool result = false; + double x = 0.0; + double y = 0.0; + + int outcode0 = out_code(x1, y1, min_x_, min_y_, max_x_, max_y_); + int outcode1 = out_code(x2, y2, min_x_, min_y_, max_x_, max_y_); + int outcodeout = 0; + + while ((outcode0 != 0) || (outcode1 != 0)) + { + if ((outcode0 & outcode1) != 0) + return result; + else + { + if (outcode0 != 0) + outcodeout = outcode0; + else + outcodeout = outcode1; + + double dx = (x2 - x1); + double dy = (y2 - y1); + + if ((outcodeout & e_clip_bottom) == e_clip_bottom) + { + x = x1 + dx * (min_y_ - y1) / dy; + y = min_y_; + } + else if ((outcodeout & e_clip_top) == e_clip_top) + { + x = x1 + dx * (max_y_ - y1) / dy; + y = max_y_; + } + else if ((outcodeout & e_clip_right) == e_clip_right) + { + y = y1 + dy * (max_x_ - x1) / dx; + x = max_x_; + } + else if ((outcodeout & e_clip_left) == e_clip_left) + { + y = y1 + dy * (min_x_ - x1) / dx; + x = min_x_; + } + + if (outcodeout == outcode0) + { + x1 = x; + y1 = y; + outcode0 = out_code(x1, y1, min_x_, min_y_, max_x_, max_y_); + } + else + { + x2 = x; + y2 = y; + outcode1 = out_code(x2, y2, min_x_, min_y_, max_x_, max_y_); + } + } + } + + return true; + } + + cartesian_canvas(const cartesian_canvas&); + cartesian_canvas operator=(const cartesian_canvas&); + + double width_div2_; + double height_div2_; + double min_x_; + double min_y_; + double max_x_; + double max_y_; + bitmap_image image_; + image_drawer draw_; +}; + +inline rgb_t convert_wave_length_nm_to_rgb(const double wave_length_nm) +{ + // Credits: Dan Bruton http://www.physics.sfasu.edu/astro/color.html + double red = 0.0; + double green = 0.0; + double blue = 0.0; + + if ((380.0 <= wave_length_nm) && (wave_length_nm <= 439.0)) + { + red = -(wave_length_nm - 440.0) / (440.0 - 380.0); + green = 0.0; + blue = 1.0; + } + else if ((440.0 <= wave_length_nm) && (wave_length_nm <= 489.0)) + { + red = 0.0; + green = (wave_length_nm - 440.0) / (490.0 - 440.0); + blue = 1.0; + } + else if ((490.0 <= wave_length_nm) && (wave_length_nm <= 509.0)) + { + red = 0.0; + green = 1.0; + blue = -(wave_length_nm - 510.0) / (510.0 - 490.0); + } + else if ((510.0 <= wave_length_nm) && (wave_length_nm <= 579.0)) + { + red = (wave_length_nm - 510.0) / (580.0 - 510.0); + green = 1.0; + blue = 0.0; + } + else if ((580.0 <= wave_length_nm) && (wave_length_nm <= 644.0)) + { + red = 1.0; + green = -(wave_length_nm - 645.0) / (645.0 - 580.0); + blue = 0.0; + } + else if ((645.0 <= wave_length_nm) && (wave_length_nm <= 780.0)) + { + red = 1.0; + green = 0.0; + blue = 0.0; + } + + double factor = 0.0; + + if ((380.0 <= wave_length_nm) && (wave_length_nm <= 419.0)) + factor = 0.3 + 0.7 * (wave_length_nm - 380.0) / (420.0 - 380.0); + else if ((420.0 <= wave_length_nm) && (wave_length_nm <= 700.0)) + factor = 1.0; + else if ((701.0 <= wave_length_nm) && (wave_length_nm <= 780.0)) + factor = 0.3 + 0.7 * (780.0 - wave_length_nm) / (780.0 - 700.0); + else + factor = 0.0; + + rgb_t result; + + const double gamma = 0.8; + const double intensity_max = 255.0; + + #define round(d) std::floor(d + 0.5) + + result.red = static_cast((red == 0.0) ? red : round(intensity_max * std::pow(red * factor, gamma))); + result.green = static_cast((green == 0.0) ? green : round(intensity_max * std::pow(green * factor, gamma))); + result.blue = static_cast((blue == 0.0) ? blue : round(intensity_max * std::pow(blue * factor, gamma))); + + #undef round + + return result; +} + +inline double weighted_distance(const unsigned char r0, const unsigned char g0, const unsigned char b0, + const unsigned char r1, const unsigned char g1, const unsigned char b1) +{ + const double diff_r = /*0.30 */ (r0 - r1); + const double diff_g = /*0.59 */ (g0 - g1); + const double diff_b = /*0.11 */ (b0 - b1); + + return std::sqrt((diff_r * diff_r) + (diff_g * diff_g) + (diff_b * diff_b)); +} + +inline double weighted_distance(const rgb_t c0, const rgb_t c1) +{ + return weighted_distance(c0.red, c0.green, c0.blue, + c1.red, c1.green, c1.blue); +} + +template +inline rgb_t find_nearest_color(const rgb_t& c, const Iterator begin, const Iterator end) +{ + if (0 == std::distance(begin,end)) + return c; + + double min_d = std::numeric_limits::max(); + rgb_t result = *begin; + + for (Iterator itr = begin; itr != end; ++itr) + { + if (c == (*itr)) + { + return (*itr); + } + + double curr_d = weighted_distance(c,*itr); + + if (curr_d < min_d) + { + min_d = curr_d; + result = *itr; + } + } + + return result; +} + +template