From e5ffdaf8b0b672efac3e116cb5417011659b3909 Mon Sep 17 00:00:00 2001 From: hoangfitus Date: Sat, 23 Dec 2023 13:44:54 +0700 Subject: [PATCH] Add radial gradient --- .../samples/TestCases/Firefox_logo,_2019.svg | 202 ++++++++++++++++++ .../samples/TestCases/Firefox_logo_2019.svg | 120 ----------- external/samples/gradient/linear.svg | 5 +- external/samples/gradient/radial.svg | 20 +- src/Parser.cpp | 81 ++++--- src/Renderer.cpp | 174 +++++++++++++-- src/Renderer.hpp | 14 +- src/graphics/Gradient.cpp | 4 +- src/graphics/Gradient.hpp | 7 + src/graphics/RadialGradient.cpp | 4 +- src/graphics/RadialGradient.hpp | 7 + 11 files changed, 451 insertions(+), 187 deletions(-) create mode 100644 external/samples/TestCases/Firefox_logo,_2019.svg delete mode 100644 external/samples/TestCases/Firefox_logo_2019.svg diff --git a/external/samples/TestCases/Firefox_logo,_2019.svg b/external/samples/TestCases/Firefox_logo,_2019.svg new file mode 100644 index 00000000..4848503b --- /dev/null +++ b/external/samples/TestCases/Firefox_logo,_2019.svg @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/samples/TestCases/Firefox_logo_2019.svg b/external/samples/TestCases/Firefox_logo_2019.svg deleted file mode 100644 index b5ab67e0..00000000 --- a/external/samples/TestCases/Firefox_logo_2019.svg +++ /dev/null @@ -1,120 +0,0 @@ - - - - Firefox Browser logo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Firefox Browser logo - - - - \ No newline at end of file diff --git a/external/samples/gradient/linear.svg b/external/samples/gradient/linear.svg index 04c14430..5b112a35 100644 --- a/external/samples/gradient/linear.svg +++ b/external/samples/gradient/linear.svg @@ -3,7 +3,7 @@ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - + @@ -17,9 +17,8 @@ Nguyen Van A - \ No newline at end of file diff --git a/external/samples/gradient/radial.svg b/external/samples/gradient/radial.svg index 3f828d0f..de22070e 100644 --- a/external/samples/gradient/radial.svg +++ b/external/samples/gradient/radial.svg @@ -1,14 +1,24 @@ - - - + + + + - + + + + + + Nguyen Van A + \ No newline at end of file diff --git a/src/Parser.cpp b/src/Parser.cpp index ac75982d..b1bce775 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -318,20 +318,22 @@ float Parser::getFloatAttribute(xml_node<> *node, std::string name) { float result; if (node->first_attribute(name.c_str()) == NULL) { if (std::string(node->name()).find("Gradient") != std::string::npos) { - if (name == "x1" || name == "y1" || name == "y2" || name == "fr") + if (name == "x1" || name == "y1" || name == "fr") result = 0; else if (name == "cx" || name == "cy") result = name == "cx" ? 0.5 * this->viewBox.second.x : 0.5 * this->viewBox.second.y; - else if (name == "r") + else if (name == "r") { result = sqrt((pow(this->viewBox.second.x, 2) + pow(this->viewBox.second.y, 2)) / - 2); - else if (name == "fx" || name == "fy") + 2) / + 2; + } else if (name == "fx" || name == "fy") result = name == "fx" ? getFloatAttribute(node, "cx") : getFloatAttribute(node, "cy"); else - result = this->viewBox.second.x; + result = name == "x2" ? this->viewBox.second.x + : this->viewBox.second.y; } else { if (name == "stroke-width" || name == "stroke-opacity" || name == "fill-opacity" || name == "opacity" || @@ -414,36 +416,48 @@ std::vector< Stop > Parser::getGradientStops(xml_node<> *node) { void Parser::GetGradients(xml_node<> *node) { xml_node<> *gradient_node = node->first_node(); while (gradient_node) { - if (std::string(gradient_node->name()) == "linearGradient") { + if (std::string(gradient_node->name()).find("Gradient") != + std::string::npos) { + Gradient *gradient; std::string id = getAttribute(gradient_node, "id"); std::string units = getAttribute(gradient_node, "gradientUnits"); - float x1 = getFloatAttribute(gradient_node, "x1"); - float y1 = getFloatAttribute(gradient_node, "y1"); - float x2 = getFloatAttribute(gradient_node, "x2"); - float y2 = getFloatAttribute(gradient_node, "y2"); std::vector< Stop > stops = getGradientStops(gradient_node); - std::pair< Vector2Df, Vector2Df > points = {{x1, y1}, {x2, y2}}; - Gradient *gradient = new LinearGradient(stops, points, units); - gradient->setTransforms(getTransformOrder(gradient_node)); - if (this->gradients.find(id) == this->gradients.end()) - this->gradients[id] = gradient; - } else if (std::string(gradient_node->name()) == "radialGradient") { - std::string id = getAttribute(gradient_node, "id"); - std::string units = getAttribute(gradient_node, "gradientUnits"); - float cx = getFloatAttribute(gradient_node, "cx"); - float cy = getFloatAttribute(gradient_node, "cy"); - float fx = getFloatAttribute(gradient_node, "fx"); - float fy = getFloatAttribute(gradient_node, "fy"); - float r = getFloatAttribute(gradient_node, "r"); - float fr = getFloatAttribute(gradient_node, "fr"); - std::vector< Stop > stops = getGradientStops(gradient_node); - std::pair< Vector2Df, Vector2Df > points = {{cx, cy}, {fx, fy}}; - Vector2Df radius(r, fr); - Gradient *gradient = - new RadialGradient(stops, points, radius, units); - gradient->setTransforms(getTransformOrder(gradient_node)); - if (this->gradients.find(id) == this->gradients.end()) - this->gradients[id] = gradient; + std::string href = getAttribute(gradient_node, "xlink:href"); + int pos = href.find("#"); + if (pos != std::string::npos) { + href = href.substr(pos + 1); + } + if (std::string(gradient_node->name()).find("linear") != + std::string::npos) { + float x1 = getFloatAttribute(gradient_node, "x1"); + float y1 = getFloatAttribute(gradient_node, "y1"); + float x2 = getFloatAttribute(gradient_node, "x2"); + float y2 = getFloatAttribute(gradient_node, "y2"); + std::pair< Vector2Df, Vector2Df > points = {{x1, y1}, {x2, y2}}; + gradient = new LinearGradient(stops, points, units); + if (this->gradients.find(id) == this->gradients.end()) + this->gradients[id] = gradient; + } else if (std::string(gradient_node->name()).find("radial") != + std::string::npos) { + float cx = getFloatAttribute(gradient_node, "cx"); + float cy = getFloatAttribute(gradient_node, "cy"); + float fx = getFloatAttribute(gradient_node, "fx"); + float fy = getFloatAttribute(gradient_node, "fy"); + float r = getFloatAttribute(gradient_node, "r"); + float fr = getFloatAttribute(gradient_node, "fr"); + std::pair< Vector2Df, Vector2Df > points = {{cx, cy}, {fx, fy}}; + Vector2Df radius(r, fr); + gradient = new RadialGradient(stops, points, radius, units); + if (this->gradients.find(id) == this->gradients.end()) + this->gradients[id] = gradient; + } + if (href != "") { + for (auto stop : parseGradient(href)->getStops()) { + gradient->addStop(stop); + } + } + if (gradient != NULL) + gradient->setTransforms(getTransformOrder(gradient_node)); } gradient_node = gradient_node->next_sibling(); } @@ -620,7 +634,8 @@ std::vector< std::string > Parser::getTransformOrder(xml_node<> *node) { while (ss >> type) { if (type.find("translate") != std::string::npos || type.find("scale") != std::string::npos || - type.find("rotate") != std::string::npos) { + type.find("rotate") != std::string::npos || + type.find("matrix") != std::string::npos) { while (type.find(")") == std::string::npos) { std::string temp; ss >> temp; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 48bc3883..1b2ba975 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -1,5 +1,7 @@ #include "Renderer.hpp" +#include + Renderer* Renderer::instance = nullptr; Renderer::Renderer() {} @@ -133,9 +135,25 @@ void Renderer::drawRectangle(Gdiplus::Graphics& graphics, path.AddArc(x + width - dx, y + height - dy, dx, dy, 0, 90); path.AddArc(x, y + height - dy, dx, dy, 90, 90); path.CloseFigure(); + if (Gdiplus::PathGradientBrush* brush = + dynamic_cast< Gdiplus::PathGradientBrush* >(rect_fill)) { + mColor color = + rectangle->getGradient()->getStops().back().getColor(); + Gdiplus::SolidBrush corner_fill( + Gdiplus::Color(color.a, color.r, color.g, color.b)); + graphics.FillPath(&corner_fill, &path); + } graphics.FillPath(rect_fill, &path); graphics.DrawPath(&rect_outline, &path); } else { + if (Gdiplus::PathGradientBrush* brush = + dynamic_cast< Gdiplus::PathGradientBrush* >(rect_fill)) { + mColor color = + rectangle->getGradient()->getStops().back().getColor(); + Gdiplus::SolidBrush corner_fill( + Gdiplus::Color(color.a, color.r, color.g, color.b)); + graphics.FillRectangle(&corner_fill, x, y, width, height); + } graphics.FillRectangle(rect_fill, x, y, width, height); graphics.DrawRectangle(&rect_outline, x, y, width, height); } @@ -153,6 +171,16 @@ void Renderer::drawCircle(Gdiplus::Graphics& graphics, Circle* circle) const { Gdiplus::RectF bound(min_bound.x, min_bound.y, max_bound.x - min_bound.x, max_bound.y - min_bound.y); Gdiplus::Brush* circle_fill = getBrush(circle, bound); + if (Gdiplus::PathGradientBrush* brush = + dynamic_cast< Gdiplus::PathGradientBrush* >(circle_fill)) { + mColor color = circle->getGradient()->getStops().back().getColor(); + Gdiplus::SolidBrush corner_fill( + Gdiplus::Color(color.a, color.r, color.g, color.b)); + graphics.FillEllipse( + &corner_fill, circle->getPosition().x - circle->getRadius().x, + circle->getPosition().y - circle->getRadius().y, + circle->getRadius().x * 2, circle->getRadius().y * 2); + } graphics.FillEllipse(circle_fill, circle->getPosition().x - circle->getRadius().x, circle->getPosition().y - circle->getRadius().y, @@ -175,6 +203,16 @@ void Renderer::drawEllipse(Gdiplus::Graphics& graphics, Ell* ellipse) const { Gdiplus::RectF bound(min_bound.x, min_bound.y, max_bound.x - min_bound.x, max_bound.y - min_bound.y); Gdiplus::Brush* ellipse_fill = getBrush(ellipse, bound); + if (Gdiplus::PathGradientBrush* brush = + dynamic_cast< Gdiplus::PathGradientBrush* >(ellipse_fill)) { + mColor color = ellipse->getGradient()->getStops().back().getColor(); + Gdiplus::SolidBrush corner_fill( + Gdiplus::Color(color.a, color.r, color.g, color.b)); + graphics.FillEllipse( + &corner_fill, ellipse->getPosition().x - ellipse->getRadius().x, + ellipse->getPosition().y - ellipse->getRadius().y, + ellipse->getRadius().x * 2, ellipse->getRadius().y * 2); + } graphics.FillEllipse( ellipse_fill, ellipse->getPosition().x - ellipse->getRadius().x, ellipse->getPosition().y - ellipse->getRadius().y, @@ -211,6 +249,13 @@ void Renderer::drawPolygon(Gdiplus::Graphics& graphics, Plygon* polygon) const { Gdiplus::RectF bound(min_bound.x, min_bound.y, max_bound.x - min_bound.x, max_bound.y - min_bound.y); Gdiplus::Brush* polygon_fill = getBrush(polygon, bound); + if (Gdiplus::PathGradientBrush* brush = + dynamic_cast< Gdiplus::PathGradientBrush* >(polygon_fill)) { + mColor color = polygon->getGradient()->getStops().back().getColor(); + Gdiplus::SolidBrush corner_fill( + Gdiplus::Color(color.a, color.r, color.g, color.b)); + graphics.FillPolygon(&corner_fill, points, idx, fill_mode); + } graphics.FillPolygon(polygon_fill, points, idx, fill_mode); graphics.DrawPolygon(&polygon_outline, points, idx); delete[] points; @@ -255,6 +300,13 @@ void Renderer::drawText(Gdiplus::Graphics& graphics, Text* text) const { Gdiplus::RectF bound; path.GetBounds(&bound); Gdiplus::Brush* text_fill = getBrush(text, bound); + if (Gdiplus::PathGradientBrush* brush = + dynamic_cast< Gdiplus::PathGradientBrush* >(text_fill)) { + mColor color = text->getGradient()->getStops().back().getColor(); + Gdiplus::SolidBrush corner_fill( + Gdiplus::Color(color.a, color.r, color.g, color.b)); + graphics.FillPath(&corner_fill, &path); + } graphics.FillPath(text_fill, &path); graphics.DrawPath(&text_outline, &path); delete text_fill; @@ -291,6 +343,13 @@ void Renderer::drawPolyline(Gdiplus::Graphics& graphics, Gdiplus::RectF bound(min_bound.x, min_bound.y, max_bound.x - min_bound.x, max_bound.y - min_bound.y); Gdiplus::Brush* polyline_fill = getBrush(polyline, bound); + // if (Gdiplus::PathGradientBrush* brush = + // dynamic_cast< Gdiplus::PathGradientBrush* >(polyline_fill)) { + // mColor color = polyline->getGradient()->getStops().back().getColor(); + // Gdiplus::SolidBrush corner_fill( + // Gdiplus::Color(color.a, color.r, color.g, color.b)); + // graphics.FillPath(&corner_fill, &path); + // } graphics.FillPath(polyline_fill, &path); graphics.DrawPath(&polyline_outline, &path); delete polyline_fill; @@ -459,6 +518,13 @@ void Renderer::drawPath(Gdiplus::Graphics& graphics, Path* path) const { Gdiplus::RectF bound; gdi_path.GetBounds(&bound); Gdiplus::Brush* path_fill = getBrush(path, bound); + if (Gdiplus::PathGradientBrush* brush = + dynamic_cast< Gdiplus::PathGradientBrush* >(path_fill)) { + mColor color = path->getGradient()->getStops().back().getColor(); + Gdiplus::SolidBrush corner_fill( + Gdiplus::Color(color.a, color.r, color.g, color.b)); + graphics.FillPath(&corner_fill, &gdi_path); + } graphics.FillPath(path_fill, &gdi_path); graphics.DrawPath(&path_outline, &gdi_path); delete path_fill; @@ -468,12 +534,18 @@ Gdiplus::Brush* Renderer::getBrush(SVGElement* shape, Gdiplus::RectF bound) const { Gradient* gradient = shape->getGradient(); if (gradient != NULL) { + std::pair< Vector2Df, Vector2Df > points = gradient->getPoints(); + std::vector< Stop > stops = gradient->getStops(); + int stop_size = stops.size() + 2; + Gdiplus::Color* colors = new Gdiplus::Color[stop_size]; + float* offsets = new float[stop_size]; if (gradient->getClass() == "LinearGradient") { - std::pair< Vector2Df, Vector2Df > points = gradient->getPoints(); - std::vector< Stop > stops = gradient->getStops(); - int stop_size = stops.size() + 2; - Gdiplus::Color* colors = new Gdiplus::Color[stop_size]; - float* offsets = new float[stop_size]; + if (gradient->getUnits() == "userSpaceOnUse") { + bound.X = points.first.x; + bound.Y = points.first.y; + bound.Width = points.second.x; + bound.Height = points.second.y; + } offsets[0] = 0; offsets[stop_size - 1] = 1; colors[0] = @@ -490,12 +562,6 @@ Gdiplus::Brush* Renderer::getBrush(SVGElement* shape, stops[i - 1].getColor().g, stops[i - 1].getColor().b); offsets[i] = stops[i - 1].getOffset(); } - if (gradient->getUnits() == "userSpaceOnUse") { - bound.X = points.first.x; - bound.Y = points.first.y; - bound.Width = points.second.x; - bound.Height = points.second.y; - } Gdiplus::LinearGradientBrush* fill = new Gdiplus::LinearGradientBrush( bound, colors[0], colors[stop_size - 1], @@ -507,22 +573,46 @@ Gdiplus::Brush* Renderer::getBrush(SVGElement* shape, delete[] offsets; return fill; } else if (gradient->getClass() == "RadialGradient") { - std::pair< Vector2Df, Vector2Df > points = gradient->getPoints(); - std::vector< Stop > stops = gradient->getStops(); + RadialGradient* radial_gradient = + dynamic_cast< RadialGradient* >(gradient); + Vector2Df radius = radial_gradient->getRadius(); + if (gradient->getUnits() == "userSpaceOnUse") { + bound.X = points.first.x - radius.x; + bound.Y = points.first.y - radius.x; + bound.Width = radius.x * 2; + bound.Height = radius.x * 2; + std::cout << bound.X << " " << bound.Y << " " << bound.Width + << " " << bound.Height << std::endl; + } Gdiplus::GraphicsPath path; - path.AddEllipse(points.first.x, points.first.y, - points.second.x - points.first.x, - points.second.y - points.first.y); + path.AddEllipse(bound); Gdiplus::PathGradientBrush* fill = new Gdiplus::PathGradientBrush(&path); - Gdiplus::Color* colors = new Gdiplus::Color[stops.size()]; - float* offsets = new float[stops.size()]; - for (size_t i = 0; i < stops.size(); ++i) { - mColor color = stops[i].getColor(); - colors[i] = Gdiplus::Color(color.a, color.r, color.g, color.b); - offsets[i] = stops[i].getOffset(); + offsets[0] = 0; + offsets[stop_size - 1] = 1; + colors[0] = Gdiplus::Color(stops[stop_size - 3].getColor().a, + stops[stop_size - 3].getColor().r, + stops[stop_size - 3].getColor().g, + stops[stop_size - 3].getColor().b); + colors[stop_size - 1] = + Gdiplus::Color(stops[0].getColor().a, stops[0].getColor().r, + stops[0].getColor().g, stops[0].getColor().b); + + for (size_t i = 1; i < stop_size - 1; ++i) { + colors[i] = + Gdiplus::Color(stops[stop_size - 2 - i].getColor().a, + stops[stop_size - 2 - i].getColor().r, + stops[stop_size - 2 - i].getColor().g, + stops[stop_size - 2 - i].getColor().b); + offsets[i] = stops[i - 1].getOffset(); + std::cout << offsets[i] << " " + << stops[stop_size - 2 - i].getColor().a << " " + << stops[stop_size - 2 - i].getColor().r << " " + << stops[stop_size - 2 - i].getColor().g << " " + << stops[stop_size - 2 - i].getColor().b << std::endl; } - fill->SetInterpolationColors(colors, offsets, stops.size()); + fill->SetInterpolationColors(colors, offsets, stop_size); + applyTransformsOnBrush(gradient->getTransforms(), fill); delete[] colors; delete[] offsets; return fill; @@ -558,4 +648,42 @@ void Renderer::applyTransformsOnBrush( } } } +} + +void Renderer::applyTransformsOnBrush( + std::vector< std::string > transform_order, + Gdiplus::PathGradientBrush*& brush) const { + for (auto type : transform_order) { + if (type.find("translate") != std::string::npos) { + float trans_x = getTranslate(type).first, + trans_y = getTranslate(type).second; + brush->TranslateTransform(trans_x, trans_y); + } else if (type.find("rotate") != std::string::npos) { + float degree = getRotate(type); + brush->RotateTransform(degree); + } else if (type.find("scale") != std::string::npos) { + if (type.find(",") != std::string::npos) { + float scale_x = getScaleXY(type).first, + scale_y = getScaleXY(type).second; + brush->ScaleTransform(scale_x, scale_y); + } else { + float scale = getScale(type); + brush->ScaleTransform(scale, scale); + } + } else if (type.find("matrix") != std::string::npos) { + float a = 0, b = 0, c = 0, d = 0, e = 0, f = 0; + if (type.find(",") != std::string::npos) { + type.erase(std::remove(type.begin(), type.end(), ','), + type.end()); + } + sscanf(type.c_str(), "matrix(%f %f %f %f %f %f)", &a, &b, &c, &d, + &e, &f); + Gdiplus::Matrix matrix(a, b, c, d, e, f); + float m[6] = {0}; + matrix.GetElements(m); + std::cout << m[0] << " " << m[1] << " " << m[2] << " " << m[3] + << " " << m[4] << " " << m[5] << std::endl; + brush->SetTransform(&matrix); + } + } } \ No newline at end of file diff --git a/src/Renderer.hpp b/src/Renderer.hpp index f0d5b33e..9289dba1 100644 --- a/src/Renderer.hpp +++ b/src/Renderer.hpp @@ -140,11 +140,23 @@ class Renderer { * * @param transform_order The order in which transformations should be * applied. - * @param brush The Gdiplus::brush object for the shape fill. + * @param brush The Gdiplus::LinearGradientBrush object for the shape fill. */ void applyTransformsOnBrush(std::vector< std::string > transform_order, Gdiplus::LinearGradientBrush*& brush) const; + /** + * @brief Utility function to apply a series of transformations to the brush + * object. + * + * @param transform_order The order in which transformations should be + * applied. + * @param brush The Gdiplus::PathGradientBrush object for the shape fill. + * + */ + void applyTransformsOnBrush(std::vector< std::string > transform_order, + Gdiplus::PathGradientBrush*& brush) const; + /** * @brief Private constructor for the Renderer class. */ diff --git a/src/graphics/Gradient.cpp b/src/graphics/Gradient.cpp index d3716cf5..caeebb67 100644 --- a/src/graphics/Gradient.cpp +++ b/src/graphics/Gradient.cpp @@ -18,4 +18,6 @@ void Gradient::setTransforms(std::vector< std::string > transforms) { std::vector< std::string > Gradient::getTransforms() const { return transforms; -} \ No newline at end of file +} + +void Gradient::addStop(Stop stop) { stops.push_back(stop); } \ No newline at end of file diff --git a/src/graphics/Gradient.hpp b/src/graphics/Gradient.hpp index 945c6575..558c90df 100644 --- a/src/graphics/Gradient.hpp +++ b/src/graphics/Gradient.hpp @@ -81,6 +81,13 @@ class Gradient { */ std::vector< std::string > getTransforms() const; + /** + * @brief Adds a stop to the gradient. + * + * @param stop The stop to be added to the gradient. + */ + void addStop(Stop stop); + private: std::vector< Stop > stops; ///< Stops of the gradient std::pair< Vector2Df, Vector2Df > diff --git a/src/graphics/RadialGradient.cpp b/src/graphics/RadialGradient.cpp index 15800518..a57bfffb 100644 --- a/src/graphics/RadialGradient.cpp +++ b/src/graphics/RadialGradient.cpp @@ -7,4 +7,6 @@ RadialGradient::RadialGradient(std::vector< Stop > stops, this->radius = radius; } -std::string RadialGradient::getClass() const { return "RadialGradient"; } \ No newline at end of file +std::string RadialGradient::getClass() const { return "RadialGradient"; } + +Vector2Df RadialGradient::getRadius() const { return radius; } \ No newline at end of file diff --git a/src/graphics/RadialGradient.hpp b/src/graphics/RadialGradient.hpp index 4d5c2821..3ca80e24 100644 --- a/src/graphics/RadialGradient.hpp +++ b/src/graphics/RadialGradient.hpp @@ -33,6 +33,13 @@ class RadialGradient : public Gradient { */ std::string getClass() const override; + /** + * @brief Gets the radius of the gradient. + * + * @return The radius of the gradient. + */ + Vector2Df getRadius() const; + private: Vector2Df radius; ///< The radius of the gradient. };