From feb71ffd55225ec3bfade72a19b23457c23ff594 Mon Sep 17 00:00:00 2001 From: quan27 <21685516+ntkwan@users.noreply.github.com> Date: Mon, 4 Dec 2023 02:30:20 +0700 Subject: [PATCH] Update shapes rendering TBD: Line, Text, Polyline (fill) --- src/Graphics.hpp | 1 - src/Parser.hpp | 16 +++- src/{graphics => }/Renderer.cpp | 108 ++++++++++++++++++++------ src/{graphics => }/Renderer.hpp | 14 +++- src/graphics/Circle.cpp | 3 - src/graphics/Circle.hpp | 7 -- src/graphics/Group.cpp | 10 +-- src/graphics/Group.hpp | 4 +- src/graphics/Rect.cpp | 2 - src/graphics/SVGElement.hpp | 11 --- src/graphics/Shape.cpp | 44 ----------- src/graphics/Shape.hpp | 130 -------------------------------- src/main.cpp | 89 +++++++++++++++++++--- 13 files changed, 201 insertions(+), 238 deletions(-) rename src/{graphics => }/Renderer.cpp (68%) rename src/{graphics => }/Renderer.hpp (70%) delete mode 100644 src/graphics/Shape.cpp delete mode 100644 src/graphics/Shape.hpp diff --git a/src/Graphics.hpp b/src/Graphics.hpp index 07cca0c4..b6711b1d 100644 --- a/src/Graphics.hpp +++ b/src/Graphics.hpp @@ -10,7 +10,6 @@ #include "graphics/Polygon.hpp" #include "graphics/Polyline.hpp" #include "graphics/Rect.hpp" -#include "graphics/Shape.hpp" #include "graphics/Text.hpp" #endif // GRAPHICS_HPP_ diff --git a/src/Parser.hpp b/src/Parser.hpp index ab0caebc..5f006eef 100644 --- a/src/Parser.hpp +++ b/src/Parser.hpp @@ -1,11 +1,25 @@ #ifndef PARSER_HPP_ #define PARSER_HPP_ +#include +#include +#include +#include +#include #include #include #include "../external/rapidxml/rapidxml.hpp" -#include "Graphics.hpp" +#include "graphics/Circle.hpp" +#include "graphics/Color.hpp" +#include "graphics/Ellipse.hpp" +#include "graphics/Group.hpp" +#include "graphics/Line.hpp" +#include "graphics/Path.hpp" +#include "graphics/Polygon.hpp" +#include "graphics/Polyline.hpp" +#include "graphics/Rect.hpp" +#include "graphics/Text.hpp" using namespace rapidxml; diff --git a/src/graphics/Renderer.cpp b/src/Renderer.cpp similarity index 68% rename from src/graphics/Renderer.cpp rename to src/Renderer.cpp index 2cb8d0c0..6b066c64 100644 --- a/src/graphics/Renderer.cpp +++ b/src/Renderer.cpp @@ -11,8 +11,39 @@ Renderer* Renderer::getInstance(Gdiplus::Graphics& graphics) { return instance; } -void Renderer::draw(Shape* shape) const { - if (shape->getClass() == "Polyline") { +void Renderer::draw(SVGElement* shape) const { + std::cout << shape->getClass() << std::endl; + if (shape->getClass() == "Group") { + Group* group = dynamic_cast< Group* >(shape); + for (auto elem : group->getElements()) { + std::cout << elem->getClass() << std::endl; + if (elem->getClass() == "Polyline") { + Plyline* polyline = dynamic_cast< Plyline* >(elem); + drawPolyline(polyline); + } else if (elem->getClass() == "Text") { + Text* text = dynamic_cast< Text* >(elem); + drawText(text); + } else if (elem->getClass() == "Rect") { + Rect* rectangle = dynamic_cast< Rect* >(elem); + drawRectangle(rectangle); + } else if (elem->getClass() == "Circle") { + Circle* circle = dynamic_cast< Circle* >(elem); + drawCircle(circle); + } else if (elem->getClass() == "Ellipse") { + Ell* ellipse = dynamic_cast< Ell* >(elem); + drawEllipse(ellipse); + } else if (elem->getClass() == "Line") { + Line* line = dynamic_cast< Line* >(elem); + drawLine(line); + } else if (elem->getClass() == "Polygon") { + Plygon* polygon = dynamic_cast< Plygon* >(elem); + drawPolygon(polygon); + } else if (elem->getClass() == "Path") { + Path* path = dynamic_cast< Path* >(elem); + drawPath(path); + } + } + } else if (shape->getClass() == "Polyline") { Plyline* polyline = dynamic_cast< Plyline* >(shape); drawPolyline(polyline); } else if (shape->getClass() == "Text") { @@ -50,45 +81,78 @@ void Renderer::drawLine(Line* line) const { void Renderer::drawRectangle(Rect* rectangle) const { mColor fill_color = rectangle->getFillColor(); - Gdiplus::SolidBrush RectFill( - Gdiplus::Color(fill_color.a, fill_color.r, fill_color.g, fill_color.b)); - graphics.FillRectangle(&RectFill, rectangle->getPosition().x, - rectangle->getPosition().y, rectangle->getWidth(), - rectangle->getHeight()); mColor outline_color = rectangle->getOutlineColor(); + Gdiplus::Pen RectOutline(Gdiplus::Color(outline_color.a, outline_color.r, outline_color.g, outline_color.b), rectangle->getOutlineThickness()); + Gdiplus::SolidBrush RectFill( + Gdiplus::Color(fill_color.a, fill_color.r, fill_color.g, fill_color.b)); graphics.DrawRectangle(&RectOutline, rectangle->getPosition().x, rectangle->getPosition().y, rectangle->getWidth(), rectangle->getHeight()); + graphics.FillRectangle(&RectFill, rectangle->getPosition().x, + rectangle->getPosition().y, rectangle->getWidth(), + rectangle->getHeight()); } -void Renderer::drawCircle(Circle* circle) const {} +void Renderer::drawCircle(Circle* circle) const { + mColor fill_color = circle->getFillColor(); + mColor outline_color = circle->getOutlineColor(); + Gdiplus::Pen circleOutline(Gdiplus::Color(outline_color.a, outline_color.r, + outline_color.g, outline_color.b), + circle->getOutlineThickness()); + Gdiplus::SolidBrush circleFill( + Gdiplus::Color(fill_color.a, fill_color.r, fill_color.g, fill_color.b)); + graphics.DrawEllipse(&circleOutline, + circle->getPosition().x - circle->getRadius().x, + circle->getPosition().y - circle->getRadius().y, + circle->getRadius().x * 2, circle->getRadius().x * 2); + graphics.FillEllipse(&circleFill, + circle->getPosition().x - circle->getRadius().x, + circle->getPosition().y - circle->getRadius().y, + circle->getRadius().x * 2, circle->getRadius().y * 2); +} -void Renderer::drawEllipse(Ell* ellipse) const {} +void Renderer::drawEllipse(Ell* ellipse) const { + mColor fill_color = ellipse->getFillColor(); + mColor outline_color = ellipse->getOutlineColor(); + Gdiplus::Pen ellipseOutline( + Gdiplus::Color(outline_color.a, outline_color.r, outline_color.g, + outline_color.b), + ellipse->getOutlineThickness()); + Gdiplus::SolidBrush ellipseFill( + Gdiplus::Color(fill_color.a, fill_color.r, fill_color.g, fill_color.b)); + + graphics.DrawEllipse( + &ellipseOutline, ellipse->getPosition().x - ellipse->getRadius().x, + ellipse->getPosition().y - ellipse->getRadius().y, + ellipse->getRadius().x * 2, ellipse->getRadius().y * 2); + graphics.FillEllipse( + &ellipseFill, ellipse->getPosition().x - ellipse->getRadius().x, + ellipse->getPosition().y - ellipse->getRadius().y, + ellipse->getRadius().x * 2, ellipse->getRadius().y * 2); +} void Renderer::drawPolygon(Plygon* polygon) const { mColor fill_color = polygon->getFillColor(); + mColor outline_color = polygon->getOutlineColor(); + Gdiplus::Pen polygonOutline( + Gdiplus::Color(outline_color.a, outline_color.r, outline_color.g, + outline_color.b), + polygon->getOutlineThickness()); Gdiplus::SolidBrush polygonFill( Gdiplus::Color(fill_color.a, fill_color.r, fill_color.g, fill_color.b)); - std::vector< Gdiplus::PointF > points; + Gdiplus::PointF* points = new Gdiplus::PointF[polygon->getPoints().size()]; + int idx = 0; const std::vector< Vector2Df >& vertices = polygon->getPoints(); - for (const Vector2Df& vertex : vertices) { - points.push_back(Gdiplus::PointF(vertex.x, vertex.y)); + for (const Vector2Df vertex : vertices) { + points[idx++] = Gdiplus::PointF(vertex.x, vertex.y); } - graphics.FillPolygon(&polygonFill, &points[0], - static_cast< int >(points.size())); - - mColor outline_color = polygon->getOutlineColor(); - Gdiplus::Pen polygonOutline( - Gdiplus::Color(outline_color.a, outline_color.r, outline_color.g, - outline_color.b), - polygon->getOutlineThickness()); - graphics.DrawPolygon(&polygonOutline, &points[0], - static_cast< int >(points.size())); + graphics.DrawPolygon(&polygonOutline, points, idx); + graphics.FillPolygon(&polygonFill, points, idx); } void Renderer::drawText(Text* text) const { diff --git a/src/graphics/Renderer.hpp b/src/Renderer.hpp similarity index 70% rename from src/graphics/Renderer.hpp rename to src/Renderer.hpp index 7cb29357..8c1eb98d 100644 --- a/src/graphics/Renderer.hpp +++ b/src/Renderer.hpp @@ -7,7 +7,17 @@ #include //clang-format on -#include "Graphics.hpp" + +#include "graphics/Circle.hpp" +#include "graphics/Color.hpp" +#include "graphics/Ellipse.hpp" +#include "graphics/Group.hpp" +#include "graphics/Line.hpp" +#include "graphics/Path.hpp" +#include "graphics/Polygon.hpp" +#include "graphics/Polyline.hpp" +#include "graphics/Rect.hpp" +#include "graphics/Text.hpp" class Renderer { public: @@ -16,7 +26,7 @@ class Renderer { void operator=(const Renderer&) = delete; Gdiplus::Graphics& graphics; - void draw(Shape* shape) const; + void draw(SVGElement* shape) const; private: void drawLine(Line* line) const; diff --git a/src/graphics/Circle.cpp b/src/graphics/Circle.cpp index 730809f4..fba26ae0 100644 --- a/src/graphics/Circle.cpp +++ b/src/graphics/Circle.cpp @@ -5,6 +5,3 @@ Circle::Circle(float radius, const Vector2Df ¢er, mColor fill, : Ell(Vector2Df(radius, radius), center, fill, stroke, stroke_width) {} std::string Circle::getClass() const { return "Circle"; } - -// void Circle::render(Renderer &renderer) const { renderer.renderCircle(*this); -// } \ No newline at end of file diff --git a/src/graphics/Circle.hpp b/src/graphics/Circle.hpp index 71f2c299..c4b22ba6 100644 --- a/src/graphics/Circle.hpp +++ b/src/graphics/Circle.hpp @@ -31,13 +31,6 @@ class Circle : public Ell { * */ std::string getClass() const override; - - // /** - // * @brief Renders the shape using the given renderer. - // * - // * @param renderer The renderer to be used for rendering the shape. - // */ - // void render(Renderer &renderer) const override; }; #endif // CIRCLE_HPP_ \ No newline at end of file diff --git a/src/graphics/Group.cpp b/src/graphics/Group.cpp index fa49ceb7..bdd46de8 100644 --- a/src/graphics/Group.cpp +++ b/src/graphics/Group.cpp @@ -12,11 +12,11 @@ Group::~Group() { std::string Group::getClass() const { return "Group"; } -// void Group::render(Renderer& renderer) const { -// for (auto shape : shapes) { -// shape->render(renderer); -// } -// } +void Group::render(Renderer& renderer) const { + for (auto shape : shapes) { + renderer.draw(shape); + } +} Attributes Group::getAttributes() const { return attributes; } diff --git a/src/graphics/Group.hpp b/src/graphics/Group.hpp index d001e597..237865e1 100644 --- a/src/graphics/Group.hpp +++ b/src/graphics/Group.hpp @@ -3,10 +3,12 @@ #include +#include "Renderer.hpp" #include "SVGElement.hpp" typedef std::vector< std::pair< std::string, std::string > > Attributes; +class Renderer; class Group : public SVGElement { public: Group(); @@ -17,7 +19,7 @@ class Group : public SVGElement { std::string getClass() const override; - // void render(Renderer& renderer) const override; + void render(Renderer& renderer) const; Attributes getAttributes() const; diff --git a/src/graphics/Rect.cpp b/src/graphics/Rect.cpp index 1d486003..1d12b540 100644 --- a/src/graphics/Rect.cpp +++ b/src/graphics/Rect.cpp @@ -15,8 +15,6 @@ Rect::Rect(float width, float height, Vector2Df position, Vector2Df radius, std::string Rect::getClass() const { return "Rect"; } -// void Rect::render(Renderer &renderer) const { renderer.renderRect(*this); } - void Rect::setWidth(float width) { this->width = width; points[1].x = width; diff --git a/src/graphics/SVGElement.hpp b/src/graphics/SVGElement.hpp index 2e570ede..263f89c6 100644 --- a/src/graphics/SVGElement.hpp +++ b/src/graphics/SVGElement.hpp @@ -5,7 +5,6 @@ #include "Color.hpp" #include "Vector2D.hpp" -class Renderer; /** * @brief Represents an element in an SVG file. @@ -33,16 +32,6 @@ class SVGElement { */ virtual std::string getClass() const = 0; - // /** - // * @brief Renders the shape using the given renderer. - // * - // * @param renderer The renderer to be used for rendering the shape. - // * - // * @note This function is pure virtual and must be implemented by derived - // * classes. - // */ - // virtual void render(Renderer& renderer) const = 0; - /** * @brief Sets the fill color of the shape. * diff --git a/src/graphics/Shape.cpp b/src/graphics/Shape.cpp deleted file mode 100644 index dcb4716c..00000000 --- a/src/graphics/Shape.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "Shape.hpp" - -Shape::Shape() - : fill(mColor::Black), stroke(mColor::Transparent), stroke_width(1) {} - -void Shape::setFillColor(const mColor& color) { fill = color; } - -const mColor& Shape::getFillColor() const { return fill; } - -void Shape::setOutlineColor(const mColor& color) { stroke = color; } - -const mColor& Shape::getOutlineColor() const { return stroke; } - -void Shape::setOutlineThickness(float thickness) { stroke_width = thickness; } - -float Shape::getOutlineThickness() const { return stroke_width; } - -void Shape::setPosition(float x, float y) { - position.x = x; - position.y = y; -} - -void Shape::setPosition(const Vector2Df& position) { - setPosition(position.x, position.y); -} - -Vector2Df Shape::getPosition() const { return position; } - -#include - -void Shape::printData() const { - std::cout << "Shape: " << getClass() << std::endl; - std::cout << "Fill: " << getFillColor() << std::endl; - std::cout << "Stroke: " << getOutlineColor() << std::endl; - std::cout << "Stroke width: " << getOutlineThickness() << std::endl; - std::cout << "Position: " << getPosition().x << " " << getPosition().y - << std::endl; -} - -void Shape::setTransforms(const std::vector< std::string >& transforms) { - this->transforms = transforms; -} - -std::vector< std::string > Shape::getTransforms() const { return transforms; } \ No newline at end of file diff --git a/src/graphics/Shape.hpp b/src/graphics/Shape.hpp deleted file mode 100644 index 00bc98e3..00000000 --- a/src/graphics/Shape.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef SHAPE_HPP_ -#define SHAPE_HPP_ - -#include - -#include "Color.hpp" -#include "Graphics.hpp" -#include "Vector2D.hpp" - -/** - * @brief Represents a shape in 2D space - * @note This class is abstract and cannot be instantiated. - * @note This class is applied Abstract Factory design pattern and used as - * interface for other shapes. - */ -class Shape { -public: - /** - * @brief Virtual constructor - */ - virtual ~Shape() = default; - - /** - * @brief Gets the type of the shape - * - * @return The type of the shape - * - * @note This function is used for determining the type of the shape. - * @note This function is pure virtual and must be implemented by derived - * classes. - */ - virtual std::string getClass() const = 0; - - /** - * @brief Sets the fill color of the shape. - * - * @param color The new fill color of the shape. - */ - void setFillColor(const mColor& color); - - /** - * @brief Sets the outline color of the shape. - * - * @param color The new outline color of the shape. - */ - void setOutlineColor(const mColor& color); - - /** - * @brief Sets the outline thickness of the shape. - * @param thickness The new outline thickness of the shape. - * @note If the thickness is negative, the outline will be inside the shape. - * If the thickness is positive, the outline will be outside the shape. - * If the thickness is zero, no outline will be drawn. - * @note The default outline thickness is 0. - * @note The outline thickness cannot be greater than the radius of the - * shape. - */ - void setOutlineThickness(float thickness); - - /** - * @brief Sets the position of the shape - * @param x The x coordinate of the new position - * @param y The y coordinate of the new position - * @note The default position of the shape is (0, 0). - * @note The position of the shape is relative to its origin. - */ - void setPosition(float x, float y); - - /** - * @brief Sets the position of the shape - * @param position The new position of the shape (Vector2f is a typedef - * of coordination vector) - * @note The default position of the shape is (0, 0). - * @note The position of the shape is relative to its origin. - */ - void setPosition(const Vector2Df& position); - - /** - * @brief Gets the fill color of the shape. - * @return The fill color of the shape. - * @note The default fill color is white. - */ - const mColor& getFillColor() const; - - /** - * @brief Gets the outline color of the shape. - * @return The outline color of the shape. - * @note The default outline color is white. - */ - const mColor& getOutlineColor() const; - - /** - * @brief Gets the outline thickness of the shape. - * @return The outline thickness of the shape. - * @note The default outline thickness is 0. - */ - float getOutlineThickness() const; - - /** - * @brief Get the current position of the shape - * - * @return The current position of the shape - * @note The default position of the shape is (0, 0). - */ - Vector2Df getPosition() const; - - virtual void printData() const; - - void setTransforms(const std::vector< std::string >& transforms); - - std::vector< std::string > getTransforms() const; - -protected: - /** - * @brief Constructs a Shape object - * @note This constructor is protected because Shape is an abstract class - * that cannot be instantiated. - */ - Shape(); - -private: - mColor fill; ///< Fill color - mColor stroke; ///< Outline color - float stroke_width; ///< Thickness of the shape's outline - Vector2Df position; ///< Position of the shape - Vector2Df origin; ///< Origin of translation/rotation/scaling of the object - std::vector< std::string > transforms; ///< List of transformations -}; - -#endif // SHAPE_HPP_ \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 09dce282..306aee68 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,14 +11,85 @@ using namespace rapidxml; -int main(int argc, char **argv) { - if (argc != 2) { - std::cout << "Usage: " << argv[0] << " " << std::endl; - return 1; - } - Parser *parser = Parser::getInstance(argv[1]); +LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +void OnPaint(HDC hdc) { + Gdiplus::Graphics graphics(hdc); + + std::string path = "external/samples/sample.svg"; + Parser *parser = Parser::getInstance(path); + Renderer *renderer = Renderer::getInstance(graphics); SVGElement *root = parser->getRoot(); root->printData(); - delete parser; - return 0; -} \ No newline at end of file + Group *group = dynamic_cast< Group * >(root); + group->render(*renderer); + // delete parser; +} + +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); + +INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow) { + HWND hWnd; + MSG msg; + WNDCLASS wndClass; + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + ULONG_PTR gdiplusToken; + + // Initialize GDI+. + GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + + wndClass.style = CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = WndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = hInstance; + wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = TEXT("svg-reader-v0.2"); + + RegisterClass(&wndClass); + + hWnd = CreateWindow(TEXT("svg-reader-v0.2"), // window class name + TEXT("svg-reader-v0.2"), // window caption + WS_OVERLAPPEDWINDOW, // window style + CW_USEDEFAULT, // initial x position + CW_USEDEFAULT, // initial y position + CW_USEDEFAULT, // initial x size + CW_USEDEFAULT, // initial y size + NULL, // parent window handle + NULL, // window menu handle + hInstance, // program instance handle + NULL); // creation parameters + + ShowWindow(hWnd, iCmdShow); + UpdateWindow(hWnd); + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + Gdiplus::GdiplusShutdown(gdiplusToken); + return msg.wParam; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam) { + HDC hdc; + PAINTSTRUCT ps; + + switch (message) { + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + OnPaint(hdc); + EndPaint(hWnd, &ps); + return 0; + case WM_DESTROY: + PostQuitMessage(0); + return 0; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } +}