diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 6383c60..91c95f0 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -16,7 +16,8 @@ add_executable(tests parser.cpp button.cpp rand.cpp - fixedpoint.cpp) + fixedpoint.cpp + pixelbuffer.cpp) target_link_libraries(tests PUBLIC diff --git a/Tests/pixelbuffer.cpp b/Tests/pixelbuffer.cpp new file mode 100644 index 0000000..c6dc91a --- /dev/null +++ b/Tests/pixelbuffer.cpp @@ -0,0 +1,250 @@ +// +// Created by erki on 13.05.21. +// + +#include "utility_fonts_5x7.hpp" +#include "utility_pixelbuffer.hpp" + +#include + +#define TEST_WIDGET "[utility],[pixelbuffer]" + +using PixelBuffer = Utility::PixelBuffer<10, 15>; +using compare_array = std::array; +using Font = Utility::Font5x7; + +TEST_CASE("Pixelbuffer fill fills the array with data.", TEST_WIDGET) +{ + SECTION("Fill works as expected") + { + PixelBuffer pb; + + pb.fill(100); + + for (const auto& px : pb) + { + REQUIRE(px == 100); + } + } + + SECTION("Default pixelbuffer is zero-filled.") + { + PixelBuffer pb; + + for (const auto& px : pb) + { + REQUIRE(px == 0); + } + } + + SECTION("Filled constructor fills the array.") + { + PixelBuffer pb(200); + + for (const auto& px : pb) + { + REQUIRE(px == 200); + } + } +} + +TEST_CASE("Coordinate access retreives the appropriate pixels.", TEST_WIDGET) +{ + PixelBuffer pb(200); + + const uint8_t* value_ptr = &pb.at(5, 2); + const uint8_t* aritm_ptr = &(*(pb.cbegin() + (5 + 2 * 10))); + + REQUIRE(value_ptr == aritm_ptr); + + const uint8_t* value_ptr_c = &pb.at({5, 2}); + + REQUIRE(value_ptr_c == aritm_ptr); +} + +TEST_CASE("Pixelbuffer draws rectangle correctly.", TEST_WIDGET) +{ + PixelBuffer pb; + + pb.rectangle({0, 0}, {5, 10}, 10); + + // clang-format off + compare_array etalon = { + 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 10, 10, 10, 10, 10, 0, 0, 0, 0, + 0 + }; + // clang-format on + + REQUIRE(pb.data() == etalon); +} + +TEST_CASE("Pixelbuffer draws line correctly.", TEST_WIDGET) +{ + PixelBuffer pb; + + SECTION("Straight line on X axis is correct.") + { + pb.line({0, 0}, {3, 0}, 10); + + compare_array etalon = { + 10, 10, 10}; + + REQUIRE(pb.data() == etalon); + } + + SECTION("Straight line on Y axis is correct.") + { + pb.line({0, 0}, {0, 3}, 10); + + // clang-format off + compare_array etalon = { + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10 + }; + // clang-format on + + REQUIRE(pb.data() == etalon); + } + + SECTION("Straight line on X axis is correct with reversed coordinates.") + { + pb.line({3, 0}, {0, 0}, 10); + + compare_array etalon = { + 10, 10, 10}; + + REQUIRE(pb.data() == etalon); + } + + SECTION("Straight line on Y axis is correct with reversed coordinates.") + { + pb.line({0, 3}, {0, 0}, 10); + + // clang-format off + compare_array etalon = { + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10 + }; + // clang-format on + + REQUIRE(pb.data() == etalon); + } + + SECTION("Diagonal line is correct.") + { + pb.line({0, 0}, {4, 4}, 10); + + // clang-format off + compare_array etalon = { + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + // clang-format on + + REQUIRE(pb.data() == etalon); + } + + SECTION("Diagonal line is correct if points are reversed.") + { + pb.line({4, 4}, {0, 0}, 10); + + // clang-format off + compare_array etalon = { + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + // clang-format on + + REQUIRE(pb.data() == etalon); + } +} + +TEST_CASE("Pixelbuffer draws circle properly.", TEST_WIDGET) +{ + PixelBuffer pb; + + pb.circle({5, 5}, 2, 10); + + // clang-format off + compare_array etalon = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 10, 10, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 10, 10, 10, 0, 0, 0 + }; + // clang-format on + + REQUIRE(pb.data() == etalon); +} + +TEST_CASE("Pixelbuffer outputs fonts correctly.", TEST_WIDGET) +{ + PixelBuffer pb; + + pb.text("!", {0, 0}, 0xFF); + + // clang-format off + compare_array etalon = { + 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + // clang-format on + + REQUIRE(pb.data() == etalon); +} + +TEST_CASE("Pixelbuffer cuts off fonts when required.", TEST_WIDGET) +{ + PixelBuffer pb; + + pb.text("!", {0, 13}, 0xFF); + + // clang-format off + compare_array etalon = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0 + }; + // clang-format on + + REQUIRE(pb.data() == etalon); +} diff --git a/Utility/Inc/utility_fonts_5x7.hpp b/Utility/Inc/utility_fonts_5x7.hpp new file mode 100644 index 0000000..341b2a1 --- /dev/null +++ b/Utility/Inc/utility_fonts_5x7.hpp @@ -0,0 +1,176 @@ +/* + * utility_fonts_5x7.hpp + * + * Created on: May 15, 2021 + * Author: erki + */ + +#ifndef SKULLC_UTILITY_FONTS_5X7_HPP_ +#define SKULLC_UTILITY_FONTS_5X7_HPP_ + +#include +#include +#include + +namespace Utility +{ + +struct Font5x7 +{ + constexpr static std::size_t width = 5; + constexpr static std::size_t height = 7; + constexpr static std::size_t bytes_per_column = 1; + + // standard ascii 5x7 font + // defines ascii characters 0x00-0x7F (0-127) + // LSByte is the first column, MSByte is last column. + // The LSbit of a byte is the top row. + static constexpr std::array data = { + 0x00, 0x00, 0x00, 0x00, 0x00,// 0x00 (nul) + 0x3E, 0x5B, 0x4F, 0x5B, 0x3E,// 0x01 (soh) + 0x3E, 0x6B, 0x4F, 0x6B, 0x3E,// 0x02 (stx) + 0x1C, 0x3E, 0x7C, 0x3E, 0x1C,// 0x03 (etx) + 0x18, 0x3C, 0x7E, 0x3C, 0x18,// 0x04 (eot) + 0x1C, 0x57, 0x7D, 0x57, 0x1C,// 0x05 (enq) + 0x1C, 0x5E, 0x7F, 0x5E, 0x1C,// 0x06 (ack) + 0x00, 0x18, 0x3C, 0x18, 0x00,// 0x07 (bel) + 0xFF, 0xE7, 0xC3, 0xE7, 0xFF,// 0x08 (bs) + 0x00, 0x18, 0x24, 0x18, 0x00,// 0x09 (tab) + 0xFF, 0xE7, 0xDB, 0xE7, 0xFF,// 0x0A (lf) + 0x30, 0x48, 0x3A, 0x06, 0x0E,// 0x0B (vt) + 0x26, 0x29, 0x79, 0x29, 0x26,// 0x0C (np) + 0x40, 0x7F, 0x05, 0x05, 0x07,// 0x0D (cr) + 0x40, 0x7F, 0x05, 0x25, 0x3F,// 0x0E (so) + 0x5A, 0x3C, 0xE7, 0x3C, 0x5A,// 0x0F (si) + 0x7F, 0x3E, 0x1C, 0x1C, 0x08,// 0x10 (dle) + 0x08, 0x1C, 0x1C, 0x3E, 0x7F,// 0x11 (dc1) + 0x14, 0x22, 0x7F, 0x22, 0x14,// 0x12 (dc2) + 0x5F, 0x5F, 0x00, 0x5F, 0x5F,// 0x13 (dc3) + 0x06, 0x09, 0x7F, 0x01, 0x7F,// 0x14 (dc4) + 0x00, 0x66, 0x89, 0x95, 0x6A,// 0x15 (nak) + 0x60, 0x60, 0x60, 0x60, 0x60,// 0x16 (syn) + 0x94, 0xA2, 0xFF, 0xA2, 0x94,// 0x17 (etb) + 0x08, 0x04, 0x7E, 0x04, 0x08,// 0x18 (can) + 0x10, 0x20, 0x7E, 0x20, 0x10,// 0x19 (em) + 0x08, 0x08, 0x2A, 0x1C, 0x08,// 0x1A (eof) + 0x08, 0x1C, 0x2A, 0x08, 0x08,// 0x1B (esc) + 0x1E, 0x10, 0x10, 0x10, 0x10,// 0x1C (fs) + 0x0C, 0x1E, 0x0C, 0x1E, 0x0C,// 0x1D (gs) + 0x30, 0x38, 0x3E, 0x38, 0x30,// 0x1E (rs) + 0x06, 0x0E, 0x3E, 0x0E, 0x06,// 0x1F (us) + 0x00, 0x00, 0x00, 0x00, 0x00,// 0x20 + 0x00, 0x00, 0x5F, 0x00, 0x00,// 0x21 ! + 0x00, 0x07, 0x00, 0x07, 0x00,// 0x22 " + 0x14, 0x7F, 0x14, 0x7F, 0x14,// 0x23 # + 0x24, 0x2A, 0x7F, 0x2A, 0x12,// 0x24 $ + 0x23, 0x13, 0x08, 0x64, 0x62,// 0x25 % + 0x36, 0x49, 0x56, 0x20, 0x50,// 0x26 & + 0x00, 0x08, 0x07, 0x03, 0x00,// 0x27 ' + 0x00, 0x1C, 0x22, 0x41, 0x00,// 0x28 ( + 0x00, 0x41, 0x22, 0x1C, 0x00,// 0x29 ) + 0x2A, 0x1C, 0x7F, 0x1C, 0x2A,// 0x2A * + 0x08, 0x08, 0x3E, 0x08, 0x08,// 0x2B + + 0x00, 0x80, 0x70, 0x30, 0x00,// 0x2C , + 0x08, 0x08, 0x08, 0x08, 0x08,// 0x2D - + 0x00, 0x00, 0x60, 0x60, 0x00,// 0x2E . + 0x20, 0x10, 0x08, 0x04, 0x02,// 0x2F / + 0x3E, 0x51, 0x49, 0x45, 0x3E,// 0x30 0 + 0x00, 0x42, 0x7F, 0x40, 0x00,// 0x31 1 + 0x72, 0x49, 0x49, 0x49, 0x46,// 0x32 2 + 0x21, 0x41, 0x49, 0x4D, 0x33,// 0x33 3 + 0x18, 0x14, 0x12, 0x7F, 0x10,// 0x34 4 + 0x27, 0x45, 0x45, 0x45, 0x39,// 0x35 5 + 0x3C, 0x4A, 0x49, 0x49, 0x31,// 0x36 6 + 0x41, 0x21, 0x11, 0x09, 0x07,// 0x37 7 + 0x36, 0x49, 0x49, 0x49, 0x36,// 0x38 8 + 0x46, 0x49, 0x49, 0x29, 0x1E,// 0x39 9 + 0x00, 0x00, 0x14, 0x00, 0x00,// 0x3A : + 0x00, 0x40, 0x34, 0x00, 0x00,// 0x3B ; + 0x00, 0x08, 0x14, 0x22, 0x41,// 0x3C < + 0x14, 0x14, 0x14, 0x14, 0x14,// 0x3D = + 0x00, 0x41, 0x22, 0x14, 0x08,// 0x3E > + 0x02, 0x01, 0x59, 0x09, 0x06,// 0x3F ? + 0x3E, 0x41, 0x5D, 0x59, 0x4E,// 0x40 @ + 0x7C, 0x12, 0x11, 0x12, 0x7C,// 0x41 A + 0x7F, 0x49, 0x49, 0x49, 0x36,// 0x42 B + 0x3E, 0x41, 0x41, 0x41, 0x22,// 0x43 C + 0x7F, 0x41, 0x41, 0x41, 0x3E,// 0x44 D + 0x7F, 0x49, 0x49, 0x49, 0x41,// 0x45 E + 0x7F, 0x09, 0x09, 0x09, 0x01,// 0x46 F + 0x3E, 0x41, 0x41, 0x51, 0x73,// 0x47 G + 0x7F, 0x08, 0x08, 0x08, 0x7F,// 0x48 H + 0x00, 0x41, 0x7F, 0x41, 0x00,// 0x49 I + 0x20, 0x40, 0x41, 0x3F, 0x01,// 0x4A J + 0x7F, 0x08, 0x14, 0x22, 0x41,// 0x4B K + 0x7F, 0x40, 0x40, 0x40, 0x40,// 0x4C L + 0x7F, 0x02, 0x1C, 0x02, 0x7F,// 0x4D M + 0x7F, 0x04, 0x08, 0x10, 0x7F,// 0x4E N + 0x3E, 0x41, 0x41, 0x41, 0x3E,// 0x4F O + 0x7F, 0x09, 0x09, 0x09, 0x06,// 0x50 P + 0x3E, 0x41, 0x51, 0x21, 0x5E,// 0x51 Q + 0x7F, 0x09, 0x19, 0x29, 0x46,// 0x52 R + 0x26, 0x49, 0x49, 0x49, 0x32,// 0x53 S + 0x03, 0x01, 0x7F, 0x01, 0x03,// 0x54 T + 0x3F, 0x40, 0x40, 0x40, 0x3F,// 0x55 U + 0x1F, 0x20, 0x40, 0x20, 0x1F,// 0x56 V + 0x3F, 0x40, 0x38, 0x40, 0x3F,// 0x57 W + 0x63, 0x14, 0x08, 0x14, 0x63,// 0x58 X + 0x03, 0x04, 0x78, 0x04, 0x03,// 0x59 Y + 0x61, 0x59, 0x49, 0x4D, 0x43,// 0x5A Z + 0x00, 0x7F, 0x41, 0x41, 0x41,// 0x5B [ + 0x02, 0x04, 0x08, 0x10, 0x20,// 0x5C backslash + 0x00, 0x41, 0x41, 0x41, 0x7F,// 0x5D ] + 0x04, 0x02, 0x01, 0x02, 0x04,// 0x5E ^ + 0x40, 0x40, 0x40, 0x40, 0x40,// 0x5F _ + 0x00, 0x03, 0x07, 0x08, 0x00,// 0x60 ` + 0x20, 0x54, 0x54, 0x78, 0x40,// 0x61 a + 0x7F, 0x28, 0x44, 0x44, 0x38,// 0x62 b + 0x38, 0x44, 0x44, 0x44, 0x28,// 0x63 c + 0x38, 0x44, 0x44, 0x28, 0x7F,// 0x64 d + 0x38, 0x54, 0x54, 0x54, 0x18,// 0x65 e + 0x00, 0x08, 0x7E, 0x09, 0x02,// 0x66 f + 0x18, 0xA4, 0xA4, 0x9C, 0x78,// 0x67 g + 0x7F, 0x08, 0x04, 0x04, 0x78,// 0x68 h + 0x00, 0x44, 0x7D, 0x40, 0x00,// 0x69 i + 0x20, 0x40, 0x40, 0x3D, 0x00,// 0x6A j + 0x7F, 0x10, 0x28, 0x44, 0x00,// 0x6B k + 0x00, 0x41, 0x7F, 0x40, 0x00,// 0x6C l + 0x7C, 0x04, 0x78, 0x04, 0x78,// 0x6D m + 0x7C, 0x08, 0x04, 0x04, 0x78,// 0x6E n + 0x38, 0x44, 0x44, 0x44, 0x38,// 0x6F o + 0xFC, 0x18, 0x24, 0x24, 0x18,// 0x70 p + 0x18, 0x24, 0x24, 0x18, 0xFC,// 0x71 q + 0x7C, 0x08, 0x04, 0x04, 0x08,// 0x72 r + 0x48, 0x54, 0x54, 0x54, 0x24,// 0x73 s + 0x04, 0x04, 0x3F, 0x44, 0x24,// 0x74 t + 0x3C, 0x40, 0x40, 0x20, 0x7C,// 0x75 u + 0x1C, 0x20, 0x40, 0x20, 0x1C,// 0x76 v + 0x3C, 0x40, 0x30, 0x40, 0x3C,// 0x77 w + 0x44, 0x28, 0x10, 0x28, 0x44,// 0x78 x + 0x4C, 0x90, 0x90, 0x90, 0x7C,// 0x79 y + 0x44, 0x64, 0x54, 0x4C, 0x44,// 0x7A z + 0x00, 0x08, 0x36, 0x41, 0x00,// 0x7B { + 0x00, 0x00, 0x77, 0x00, 0x00,// 0x7C | + 0x00, 0x41, 0x36, 0x08, 0x00,// 0x7D } + 0x02, 0x01, 0x02, 0x04, 0x02,// 0x7E ~ + 0x3C, 0x26, 0x23, 0x26, 0x3C,// 0x7F + }; + + using const_iterator = decltype(data)::const_iterator; + + static constexpr std::uint32_t bytesPerChar() + { + return sizeof(data) / 128; + } + + static constexpr std::pair getCharacterBytes(const char& c) + { + const_iterator it = data.cbegin() + ((c % 128) * 5); + return {it, it + 5}; + } +}; + +}// namespace Utility + + +#endif /* SKULLC_UTILITY_FONTS_5X7_HPP_ */ diff --git a/Utility/Inc/utility_pixelbuffer.hpp b/Utility/Inc/utility_pixelbuffer.hpp new file mode 100644 index 0000000..292679d --- /dev/null +++ b/Utility/Inc/utility_pixelbuffer.hpp @@ -0,0 +1,245 @@ +// +// Created by erki on 13.05.21. +// + +#ifndef SKULLC_UTILITY_PIXELBUFFER_HPP +#define SKULLC_UTILITY_PIXELBUFFER_HPP + +#include +#include +#include +#include + +namespace Utility +{ + +template +class PixelBuffer +{ +public: + static constexpr std::size_t width = W; + static constexpr std::size_t height = H; + using value = std::uint8_t; + using container = std::array; + using iterator = typename container::iterator; + using const_iterator = typename container::const_iterator; + using coordinates = std::pair; + + PixelBuffer() + : data_({0}) + {} + + explicit PixelBuffer(const value& fill_value) + : data_() + { + fill(fill_value); + } + + constexpr value& at(const std::size_t& x, const std::size_t& y) + { + return data_[x + ((y) *width)]; + } + + constexpr const value& at(const std::size_t& x, const std::size_t& y) const + { + return data_[x + ((y) *width)]; + } + + constexpr value& at(const coordinates& c) + { + return at(x_(c), y_(c)); + } + + constexpr const value& at(const coordinates& c) const + { + return at(x_(c), y_(c)); + } + + const container& data() const + { + return data_; + } + + const value* ptr() const + { + return data_.data(); + } + + iterator begin() + { + return data_.begin(); + } + + iterator end() + { + return data_.end(); + } + + const_iterator cbegin() const + { + return data_.cbegin(); + } + + const_iterator cend() const + { + return data_.cend(); + } + + void fill(const value& color) + { + data_.fill(color); + } + + /** + * Draws the text in the font provided onto the pixel buffer. + * + * Currently only supports fonts which are 1 byte high at most. + * + * @todo: make the function work with multi-byte fonts. + * + * @tparam Font The font object to be used. Must have the following members: + * * height, indicating how many pixels tall the font is. + * * width, indicating how many pixels wide the font is. + * * getCharacterBytes(char) that returns start and stop iterators for a given character. + */ + template + void text(const std::string_view& text, const coordinates& start, const value& color, const value& background = 0x00) + { + std::uint32_t x = x_(start); + const std::uint32_t y = y_(start); + for (const char& c : text) + { + const auto [c_start, c_stop] = Font::getCharacterBytes(c); + + for (auto it = c_start; it != c_stop; it++, x++) + { + const std::uint8_t& column_byte = *it; + + if (x >= width) + return; + + for (std::uint32_t i = 0; i < Font::height && i + y < height; i++) + { + at(x, y + i) = column_byte & (1 << i) ? color : background; + } + } + + x++; + } + } + + void rectangle(const coordinates& top_left, const coordinates& size, const value& color) + { + for (auto i = x_(top_left); i < (x_(top_left) + x_(size)); i++) + { + at(i, y_(top_left)) = color; + at(i, y_(top_left) + y_(size) - 1) = color; + } + + for (auto i = y_(top_left); i < (y_(top_left) + y_(size)); i++) + { + at(x_(top_left), i) = color; + at(x_(top_left) + x_(size) - 1, i) = color; + } + } + + void line(const coordinates& p1, const coordinates& p2, const value& color) + { + const coordinates start = {std::min(x_(p1), x_(p2)), std::min(y_(p1), y_(p2))}; + const coordinates stop = {std::max(x_(p1), x_(p2)), std::max(y_(p1), y_(p2))}; + + const auto dx = std::int32_t(x_(stop)) - std::int32_t(x_(start)); + const auto dy = std::int32_t(y_(stop)) - std::int32_t(y_(start)); + + auto x = std::int32_t(x_(start)); + auto y = std::int32_t(y_(start)); + + // We shortcut the direct lines, as Bresenham's algorithm cannot handle them. + if (dx == 0 && dy == 0) + { + at(start) = color; + } else if (dx == 0) + { + for (; y < y_(stop); y++) + at({x, y}) = color; + } else if (dy == 0) + { + for (; x < x_(stop); x++) + at({x, y}) = color; + } else + { + // Bresenham's algorithm. + std::int32_t p = 2 * dy - dx; + + while (x < x_(stop)) + { + at({x, y}) = color; + + if (p >= 0) + { + y++; + p = p + 2 * dy - 2 * dx; + } else + { + p = p + 2 * dy; + } + + x++; + } + } + } + + void circle(const coordinates& center, const std::size_t radius, const value& color) + { + auto draw_pixels = [this, color, center](const std::size_t& x, const std::size_t& y) { + at(x_(center) + x, y_(center) + y) = color; + at(x_(center) - x, y_(center) + y) = color; + at(x_(center) + x, y_(center) - y) = color; + at(x_(center) - x, y_(center) - y) = color; + + at(x_(center) + y, y_(center) + x) = color; + at(x_(center) - y, y_(center) + x) = color; + at(x_(center) + y, y_(center) - x) = color; + at(x_(center) - y, y_(center) - x) = color; + }; + + auto x = std::int32_t(0); + auto y = std::int32_t(radius); + + auto d = 3 - 2 * std::int32_t(radius); + + draw_pixels(x, y); + while (y >= x) + { + x++; + + if (d > 0) + { + y--; + d = d + 4 * (x - y) + 10; + } else + { + d = d + 4 * x + 6; + } + + draw_pixels(x, y); + } + } + +private: + container data_; + + constexpr const std::size_t& x_(const coordinates& c) const + { + return c.first; + } + + constexpr const std::size_t& y_(const coordinates& c) const + { + return c.second; + } +}; + +}// namespace Utility + +#endif//SKULLC_UTILITY_PIXELBUFFER_HPP