# HG changeset patch # User Sebastien Jodogne # Date 1520972061 -3600 # Node ID e91bab2d8c75b98662d5e6434661a52980c06bf8 # Parent 345725b9350c236b99b4752e08b377f01cf1994d Bresenham's line algorithm diff -r 345725b9350c -r e91bab2d8c75 Core/Images/ImageProcessing.cpp --- a/Core/Images/ImageProcessing.cpp Tue Mar 13 17:02:30 2018 +0100 +++ b/Core/Images/ImageProcessing.cpp Tue Mar 13 21:14:21 2018 +0100 @@ -34,7 +34,7 @@ #include "../PrecompiledHeaders.h" #include "ImageProcessing.h" -#include "../OrthancException.h" +#include "PixelTraits.h" #include @@ -229,8 +229,8 @@ template - void MultiplyConstantInternal(ImageAccessor& image, - float factor) + static void MultiplyConstantInternal(ImageAccessor& image, + float factor) { if (std::abs(factor - 1.0f) <= std::numeric_limits::epsilon()) { @@ -277,9 +277,9 @@ template - void ShiftScaleInternal(ImageAccessor& image, - float offset, - float scaling) + static void ShiftScaleInternal(ImageAccessor& image, + float offset, + float scaling) { const float minFloatValue = static_cast(std::numeric_limits::min()); const float maxFloatValue = static_cast(std::numeric_limits::max()); @@ -927,4 +927,222 @@ throw OrthancException(ErrorCode_NotImplemented); } } + + + + namespace + { + template + class BresenhamPixelWriter + { + private: + typedef typename PixelTraits::PixelType PixelType; + + Orthanc::ImageAccessor& image_; + PixelType value_; + + void PlotLineLow(int x0, + int y0, + int x1, + int y1) + { + int dx = x1 - x0; + int dy = y1 - y0; + int yi = 1; + + if (dy < 0) + { + yi = -1; + dy = -dy; + } + + int d = 2 * dy - dx; + int y = y0; + + for (int x = x0; x <= x1; x++) + { + Write(x, y); + + if (d > 0) + { + y = y + yi; + d = d - 2 * dx; + } + + d = d + 2*dy; + } + } + + void PlotLineHigh(int x0, + int y0, + int x1, + int y1) + { + int dx = x1 - x0; + int dy = y1 - y0; + int xi = 1; + + if (dx < 0) + { + xi = -1; + dx = -dx; + } + + int d = 2 * dx - dy; + int x = x0; + + for (int y = y0; y <= y1; y++) + { + Write(x, y); + + if (d > 0) + { + x = x + xi; + d = d - 2 * dy; + } + + d = d + 2 * dx; + } + } + + public: + BresenhamPixelWriter(Orthanc::ImageAccessor& image, + int64_t value) : + image_(image), + value_(PixelTraits::IntegerToPixel(value)) + { + } + + BresenhamPixelWriter(Orthanc::ImageAccessor& image, + const PixelType& value) : + image_(image), + value_(value) + { + } + + void Write(int x, + int y) + { + if (x >= 0 && + y >= 0 && + static_cast(x) < image_.GetWidth() && + static_cast(y) < image_.GetHeight()) + { + PixelType* p = reinterpret_cast(image_.GetRow(y)); + p[x] = value_; + } + } + + void DrawSegment(int x0, + int y0, + int x1, + int y1) + { + // This is an implementation of Bresenham's line algorithm + // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#All_cases + + if (abs(y1 - y0) < abs(x1 - x0)) + { + if (x0 > x1) + { + PlotLineLow(x1, y1, x0, y0); + } + else + { + PlotLineLow(x0, y0, x1, y1); + } + } + else + { + if (y0 > y1) + { + PlotLineHigh(x1, y1, x0, y0); + } + else + { + PlotLineHigh(x0, y0, x1, y1); + } + } + } + }; + } + + + void ImageProcessing::DrawLineSegment(ImageAccessor& image, + int x0, + int y0, + int x1, + int y1, + int64_t value) + { + switch (image.GetFormat()) + { + case Orthanc::PixelFormat_Grayscale8: + { + BresenhamPixelWriter writer(image, value); + writer.DrawSegment(x0, y0, x1, y1); + break; + } + + case Orthanc::PixelFormat_Grayscale16: + { + BresenhamPixelWriter writer(image, value); + writer.DrawSegment(x0, y0, x1, y1); + break; + } + + case Orthanc::PixelFormat_SignedGrayscale16: + { + BresenhamPixelWriter writer(image, value); + writer.DrawSegment(x0, y0, x1, y1); + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + + void ImageProcessing::DrawLineSegment(ImageAccessor& image, + int x0, + int y0, + int x1, + int y1, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha) + { + switch (image.GetFormat()) + { + case Orthanc::PixelFormat_BGRA32: + { + PixelTraits::PixelType pixel; + pixel.red_ = red; + pixel.green_ = green; + pixel.blue_ = blue; + pixel.alpha_ = alpha; + + BresenhamPixelWriter writer(image, pixel); + writer.DrawSegment(x0, y0, x1, y1); + break; + } + + case Orthanc::PixelFormat_RGB24: + { + PixelTraits::PixelType pixel; + pixel.red_ = red; + pixel.green_ = green; + pixel.blue_ = blue; + + BresenhamPixelWriter writer(image, pixel); + writer.DrawSegment(x0, y0, x1, y1); + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } } diff -r 345725b9350c -r e91bab2d8c75 Core/Images/ImageProcessing.h --- a/Core/Images/ImageProcessing.h Tue Mar 13 17:02:30 2018 +0100 +++ b/Core/Images/ImageProcessing.h Tue Mar 13 21:14:21 2018 +0100 @@ -39,49 +39,65 @@ namespace Orthanc { - class ImageProcessing + namespace ImageProcessing { - public: - static void Copy(ImageAccessor& target, - const ImageAccessor& source); + void Copy(ImageAccessor& target, + const ImageAccessor& source); - static void Convert(ImageAccessor& target, - const ImageAccessor& source); + void Convert(ImageAccessor& target, + const ImageAccessor& source); - static void Set(ImageAccessor& image, - int64_t value); + void Set(ImageAccessor& image, + int64_t value); - static void Set(ImageAccessor& image, - uint8_t red, - uint8_t green, - uint8_t blue, - uint8_t alpha); + void Set(ImageAccessor& image, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha); - static void ShiftRight(ImageAccessor& target, - unsigned int shift); + void ShiftRight(ImageAccessor& target, + unsigned int shift); - static void GetMinMaxIntegerValue(int64_t& minValue, - int64_t& maxValue, - const ImageAccessor& image); + void GetMinMaxIntegerValue(int64_t& minValue, + int64_t& maxValue, + const ImageAccessor& image); - static void GetMinMaxFloatValue(float& minValue, - float& maxValue, - const ImageAccessor& image); + void GetMinMaxFloatValue(float& minValue, + float& maxValue, + const ImageAccessor& image); - static void AddConstant(ImageAccessor& image, - int64_t value); + void AddConstant(ImageAccessor& image, + int64_t value); // "useRound" is expensive - static void MultiplyConstant(ImageAccessor& image, - float factor, - bool useRound); + void MultiplyConstant(ImageAccessor& image, + float factor, + bool useRound); // "useRound" is expensive - static void ShiftScale(ImageAccessor& image, - float offset, - float scaling, - bool useRound); + void ShiftScale(ImageAccessor& image, + float offset, + float scaling, + bool useRound); + + void Invert(ImageAccessor& image); - static void Invert(ImageAccessor& image); + void DrawLineSegment(ImageAccessor& image, + int x0, + int y0, + int x1, + int y1, + int64_t value); + + void DrawLineSegment(ImageAccessor& image, + int x0, + int y0, + int x1, + int y1, + uint8_t red, + uint8_t green, + uint8_t blue, + uint8_t alpha); }; } diff -r 345725b9350c -r e91bab2d8c75 Core/Images/PixelTraits.h --- a/Core/Images/PixelTraits.h Tue Mar 13 17:02:30 2018 +0100 +++ b/Core/Images/PixelTraits.h Tue Mar 13 21:14:21 2018 +0100 @@ -34,23 +34,36 @@ #pragma once #include "../Enumerations.h" +#include "../OrthancException.h" +#include namespace Orthanc { - template - struct PixelTraits; - - - template <> - struct PixelTraits + template + struct IntegerPixelTraits { - typedef uint8_t PixelType; + typedef _PixelType PixelType; ORTHANC_FORCE_INLINE static PixelFormat GetPixelFormat() { - return PixelFormat_Grayscale8; + return format; + } + + ORTHANC_FORCE_INLINE + static PixelType IntegerToPixel(int64_t value) + { + if (value < static_cast(std::numeric_limits::min()) || + value > static_cast(std::numeric_limits::max())) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + return static_cast(value); + } } ORTHANC_FORCE_INLINE @@ -60,6 +73,18 @@ } ORTHANC_FORCE_INLINE + static void SetMinValue(PixelType& target) + { + target = std::numeric_limits::min(); + } + + ORTHANC_FORCE_INLINE + static void SetMaxValue(PixelType& target) + { + target = std::numeric_limits::max(); + } + + ORTHANC_FORCE_INLINE static void Copy(PixelType& target, const PixelType& source) { @@ -76,7 +101,18 @@ static void FloatToPixel(PixelType& target, float value) { - target = static_cast(value); + if (value < static_cast(std::numeric_limits::min())) + { + target = std::numeric_limits::min(); + } + else if (value > static_cast(std::numeric_limits::max())) + { + target = std::numeric_limits::max(); + } + else + { + target = static_cast(value); + } } ORTHANC_FORCE_INLINE @@ -88,95 +124,28 @@ }; - template <> - struct PixelTraits - { - typedef uint16_t PixelType; - - ORTHANC_FORCE_INLINE - static PixelFormat GetPixelFormat() - { - return PixelFormat_Grayscale16; - } - - ORTHANC_FORCE_INLINE - static void SetZero(PixelType& target) - { - target = 0; - } - - ORTHANC_FORCE_INLINE - static void Copy(PixelType& target, - const PixelType& source) - { - target = source; - } - - ORTHANC_FORCE_INLINE - static float PixelToFloat(const PixelType& source) - { - return static_cast(source); - } - - ORTHANC_FORCE_INLINE - static void FloatToPixel(PixelType& target, - float value) - { - target = static_cast(value); - } - - ORTHANC_FORCE_INLINE - static bool IsEqual(const PixelType& a, - const PixelType& b) - { - return a == b; - } - }; + template + struct PixelTraits; template <> - struct PixelTraits + struct PixelTraits : + public IntegerPixelTraits { - typedef int16_t PixelType; - - ORTHANC_FORCE_INLINE - static PixelFormat GetPixelFormat() - { - return PixelFormat_SignedGrayscale16; - } - - ORTHANC_FORCE_INLINE - static void SetZero(PixelType& target) - { - target = 0; - } + }; - ORTHANC_FORCE_INLINE - static void Copy(PixelType& target, - const PixelType& source) - { - target = source; - } - - ORTHANC_FORCE_INLINE - static float PixelToFloat(const PixelType& source) - { - return static_cast(source); - } + + template <> + struct PixelTraits : + public IntegerPixelTraits + { + }; - ORTHANC_FORCE_INLINE - static void FloatToPixel(PixelType& target, - float value) - { - target = static_cast(value); - } - - ORTHANC_FORCE_INLINE - static bool IsEqual(const PixelType& a, - const PixelType& b) - { - return a == b; - } + + template <> + struct PixelTraits : + public IntegerPixelTraits + { };