diff --git a/Libraries/LibGfx/Forward.h b/Libraries/LibGfx/Forward.h index 37946b3b43b..6a6378c9b23 100644 --- a/Libraries/LibGfx/Forward.h +++ b/Libraries/LibGfx/Forward.h @@ -42,6 +42,7 @@ class ImageDecoder; class Painter; class Palette; class PaletteImpl; +class Path; class Point; class Rect; class ShareableBitmap; diff --git a/Libraries/LibGfx/Makefile b/Libraries/LibGfx/Makefile index 2645693ceee..201338c4018 100644 --- a/Libraries/LibGfx/Makefile +++ b/Libraries/LibGfx/Makefile @@ -11,6 +11,7 @@ OBJS = \ PNGLoader.o \ Painter.o \ Palette.o \ + Path.o \ Point.o \ Rect.o \ ShareableBitmap.o \ diff --git a/Libraries/LibGfx/Painter.cpp b/Libraries/LibGfx/Painter.cpp index 2033f0fac7c..a9b57307786 100644 --- a/Libraries/LibGfx/Painter.cpp +++ b/Libraries/LibGfx/Painter.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -1010,4 +1011,24 @@ PainterStateSaver::~PainterStateSaver() m_painter.restore(); } +void Painter::stroke_path(const Path& path, Color color, int thickness) +{ + FloatPoint cursor; + + for (auto& segment : path.segments()) { + switch (segment.type) { + case Path::Segment::Type::Invalid: + ASSERT_NOT_REACHED(); + break; + case Path::Segment::Type::MoveTo: + cursor = segment.point; + break; + case Path::Segment::Type::LineTo: + draw_line(Point(cursor.x(), cursor.y()), Point(segment.point.x(), segment.point.y()), color, thickness); + cursor = segment.point; + break; + } + } +} + } diff --git a/Libraries/LibGfx/Painter.h b/Libraries/LibGfx/Painter.h index b4923bded1b..61b974a5598 100644 --- a/Libraries/LibGfx/Painter.h +++ b/Libraries/LibGfx/Painter.h @@ -69,6 +69,8 @@ public: void draw_emoji(const Point&, const Gfx::Bitmap&, const Font&); void draw_glyph_or_emoji(const Point&, u32 codepoint, const Font&, Color); + void stroke_path(const Path&, Color, int thickness); + const Font& font() const { return *state().font; } void set_font(const Font& font) { state().font = &font; } diff --git a/Libraries/LibGfx/Path.cpp b/Libraries/LibGfx/Path.cpp new file mode 100644 index 00000000000..cd7e3dfcb83 --- /dev/null +++ b/Libraries/LibGfx/Path.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace Gfx { + +void Path::close() +{ + if (m_segments.size() <= 1) + return; + + auto& last_point = m_segments.last().point; + + for (ssize_t i = m_segments.size() - 1; i >= 0; --i) { + auto& segment = m_segments[i]; + if (segment.type == Segment::Type::MoveTo) { + if (last_point == segment.point) + return; + m_segments.append({ Segment::Type::LineTo, segment.point }); + return; + } + } +} + +String Path::to_string() const +{ + StringBuilder builder; + builder.append("Path { "); + for (auto& segment : m_segments) { + switch (segment.type) { + case Segment::Type::MoveTo: + builder.append("MoveTo"); + break; + case Segment::Type::LineTo: + builder.append("LineTo"); + break; + case Segment::Type::Invalid: + builder.append("Invalid"); + break; + } + builder.append('('); + builder.append(segment.point.to_string()); + builder.append(')'); + + builder.append(' '); + } + builder.append("}"); + return builder.to_string(); +} + +} diff --git a/Libraries/LibGfx/Path.h b/Libraries/LibGfx/Path.h new file mode 100644 index 00000000000..2d9c74495cb --- /dev/null +++ b/Libraries/LibGfx/Path.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include + +namespace Gfx { + +class Path { +public: + struct Segment { + enum class Type { + Invalid, + MoveTo, + LineTo, + }; + + Type type { Type::Invalid }; + FloatPoint point; + }; + + Path() {} + + void move_to(const FloatPoint& point) + { + m_segments.append({ Segment::Type::MoveTo, point }); + } + + void line_to(const FloatPoint& point) + { + m_segments.append({ Segment::Type::LineTo, point }); + } + + void close(); + + const Vector& segments() const { return m_segments; } + + String to_string() const; + +private: + Vector m_segments; +}; + +inline const LogStream& operator<<(const LogStream& stream, const Path& path) +{ + return stream << path.to_string(); +} + +}