mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:22:46 +00:00 
			
		
		
		
	LibWeb: Begin SVG element support
This commit starts adding a basic SVG element. Currently, svg elements have support for the width and height properties, as well as the stroke, stroke-width, and fill properties. The only child element supported is the path element, as most other graphical elements are just shorthand for paths.
This commit is contained in:
		
							parent
							
								
									bbcdab2baa
								
							
						
					
					
						commit
						22f0953fe2
					
				
					 11 changed files with 1013 additions and 2 deletions
				
			
		|  | @ -1412,7 +1412,7 @@ void Painter::fill_path(Path& path, Color color, WindingRule winding_rule) | |||
|             }); | ||||
| #ifdef FILL_PATH_DEBUG | ||||
|             if ((int)scanline % 10 == 0) { | ||||
|                 draw_text(Rect(active_list.last().x - 20, scanline, 20, 10), String::format("%d", (int)scanline)); | ||||
|                 draw_text(IntRect(active_list.last().x - 20, scanline, 20, 10), String::format("%d", (int)scanline)); | ||||
|             } | ||||
| #endif | ||||
| 
 | ||||
|  | @ -1499,7 +1499,7 @@ void Painter::fill_path(Path& path, Color color, WindingRule winding_rule) | |||
| #ifdef FILL_PATH_DEBUG | ||||
|     size_t i { 0 }; | ||||
|     for (auto& segment : segments) | ||||
|         draw_line(Point(segment.from.x(), segment.from.y()), Point(segment.to.x(), segment.to.y()), Color::from_hsv(++i / segments.size() * 255, 255, 255), 1); | ||||
|         draw_line(IntPoint(segment.from.x(), segment.from.y()), IntPoint(segment.to.x(), segment.to.y()), Color::from_hsv(++i / segments.size() * 255, 255, 255), 1); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -46,8 +46,10 @@ set(SOURCES | |||
|     DOM/HTMLInputElement.cpp | ||||
|     DOM/HTMLObjectElement.cpp | ||||
|     DOM/HTMLLinkElement.cpp | ||||
|     DOM/HTMLPathElement.cpp | ||||
|     DOM/HTMLScriptElement.cpp | ||||
|     DOM/HTMLStyleElement.cpp | ||||
|     DOM/HTMLSvgElement.cpp | ||||
|     DOM/HTMLTableElement.cpp | ||||
|     DOM/HTMLTableCellElement.cpp | ||||
|     DOM/HTMLTableRowElement.cpp | ||||
|  | @ -79,6 +81,7 @@ set(SOURCES | |||
|     Layout/LayoutNode.cpp | ||||
|     Layout/LayoutPosition.cpp | ||||
|     Layout/LayoutReplaced.cpp | ||||
|     Layout/LayoutSvg.cpp | ||||
|     Layout/LayoutTable.cpp | ||||
|     Layout/LayoutTableCell.cpp | ||||
|     Layout/LayoutTableRow.cpp | ||||
|  |  | |||
|  | @ -41,8 +41,10 @@ | |||
| #include <LibWeb/DOM/HTMLInputElement.h> | ||||
| #include <LibWeb/DOM/HTMLLinkElement.h> | ||||
| #include <LibWeb/DOM/HTMLObjectElement.h> | ||||
| #include <LibWeb/DOM/HTMLPathElement.h> | ||||
| #include <LibWeb/DOM/HTMLScriptElement.h> | ||||
| #include <LibWeb/DOM/HTMLStyleElement.h> | ||||
| #include <LibWeb/DOM/HTMLSvgElement.h> | ||||
| #include <LibWeb/DOM/HTMLTableCellElement.h> | ||||
| #include <LibWeb/DOM/HTMLTableElement.h> | ||||
| #include <LibWeb/DOM/HTMLTableRowElement.h> | ||||
|  | @ -97,6 +99,10 @@ NonnullRefPtr<Element> create_element(Document& document, const FlyString& tag_n | |||
|         return adopt(*new HTMLCanvasElement(document, lowercase_tag_name)); | ||||
|     if (lowercase_tag_name == HTML::TagNames::object) | ||||
|         return adopt(*new HTMLObjectElement(document, lowercase_tag_name)); | ||||
|     if (lowercase_tag_name == HTML::TagNames::svg) | ||||
|         return adopt(*new HTMLSvgElement(document, lowercase_tag_name)); | ||||
|     if (lowercase_tag_name == HTML::TagNames::path) | ||||
|         return adopt(*new HTMLPathElement(document, lowercase_tag_name)); | ||||
|     return adopt(*new Element(document, lowercase_tag_name)); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										499
									
								
								Libraries/LibWeb/DOM/HTMLPathElement.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										499
									
								
								Libraries/LibWeb/DOM/HTMLPathElement.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,499 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||
|  * 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 <AK/StringBuilder.h> | ||||
| #include <LibGfx/Path.h> | ||||
| #include <LibWeb/DOM/Document.h> | ||||
| #include <LibWeb/DOM/Event.h> | ||||
| #include <LibWeb/DOM/HTMLPathElement.h> | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| //#define PATH_DEBUG
 | ||||
| 
 | ||||
| namespace Web { | ||||
| 
 | ||||
| PathDataParser::PathDataParser(const String& source) | ||||
|     : m_source(source) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| Vector<PathInstruction> PathDataParser::parse() | ||||
| { | ||||
|     parse_whitespace(); | ||||
|     while (!done()) | ||||
|         parse_drawto(); | ||||
|     if (!m_instructions.is_empty() && m_instructions[0].type != PathInstructionType::Move) | ||||
|         ASSERT_NOT_REACHED(); | ||||
|     return m_instructions; | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_drawto() { | ||||
|     if (match('M') || match('m')) { | ||||
|         parse_moveto(); | ||||
|     } else if (match('Z') || match('z')) { | ||||
|         parse_closepath(); | ||||
|     } else if (match('L') || match('l')) { | ||||
|         parse_lineto(); | ||||
|     } else if (match('H') || match('h')) { | ||||
|         parse_horizontal_lineto(); | ||||
|     } else if (match('V') || match('v')) { | ||||
|         parse_vertical_lineto(); | ||||
|     } else if (match('C') || match('c')) { | ||||
|         parse_curveto(); | ||||
|     } else if (match('S') || match('s')) { | ||||
|         parse_smooth_curveto(); | ||||
|     } else if (match('Q') || match('q')) { | ||||
|         parse_quadratic_bezier_curveto(); | ||||
|     } else if (match('T') || match('t')) { | ||||
|         parse_smooth_quadratic_bezier_curveto(); | ||||
|     } else if (match('A') || match('a')) { | ||||
|         parse_elliptical_arc(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_moveto() | ||||
| { | ||||
|     bool absolute = consume() == 'M'; | ||||
|     parse_whitespace(); | ||||
|     for (auto& pair : parse_coordinate_pair_sequence()) | ||||
|         m_instructions.append({ PathInstructionType::Move, absolute, pair }); | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_closepath() | ||||
| { | ||||
|     bool absolute = consume() == 'Z'; | ||||
|     m_instructions.append({ PathInstructionType::ClosePath, absolute, {} }); | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_lineto() | ||||
| { | ||||
|     bool absolute = consume() == 'L'; | ||||
|     parse_whitespace(); | ||||
|     for (auto& pair : parse_coordinate_pair_sequence()) | ||||
|         m_instructions.append({ PathInstructionType::Line, absolute, pair }); | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_horizontal_lineto() | ||||
| { | ||||
|     bool absolute = consume() == 'H'; | ||||
|     parse_whitespace(); | ||||
|     m_instructions.append({ PathInstructionType::HorizontalLine, absolute, parse_coordinate_sequence() }); | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_vertical_lineto() | ||||
| { | ||||
|     bool absolute = consume() == 'V'; | ||||
|     parse_whitespace(); | ||||
|     m_instructions.append({ PathInstructionType::VerticalLine, absolute, parse_coordinate_sequence() }); | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_curveto() | ||||
| { | ||||
|     bool absolute = consume() == 'C'; | ||||
|     parse_whitespace(); | ||||
| 
 | ||||
|     while (true) { | ||||
|         m_instructions.append({ PathInstructionType::Curve, absolute, parse_coordinate_pair_triplet() }); | ||||
|         parse_whitespace(); | ||||
|         if (!match_number()) | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_smooth_curveto() | ||||
| { | ||||
|     bool absolute = consume() == 'S'; | ||||
|     parse_whitespace(); | ||||
| 
 | ||||
|     while (true) { | ||||
|         m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() }); | ||||
|         parse_whitespace(); | ||||
|         if (!match_number()) | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_quadratic_bezier_curveto() | ||||
| { | ||||
|     bool absolute = consume() == 'Q'; | ||||
|     parse_whitespace(); | ||||
| 
 | ||||
|     while (true) { | ||||
|         m_instructions.append({ PathInstructionType::QuadraticBezierCurve, absolute, parse_coordinate_pair_double() }); | ||||
|         parse_whitespace(); | ||||
|         if (!match_number()) | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_smooth_quadratic_bezier_curveto() | ||||
| { | ||||
|     bool absolute = consume() == 'T'; | ||||
|     parse_whitespace(); | ||||
| 
 | ||||
|     while (true) { | ||||
|         m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair_double() }); | ||||
|         parse_whitespace(); | ||||
|         if (!match_number()) | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_elliptical_arc() | ||||
| { | ||||
|     bool absolute = consume() == 'A'; | ||||
|     parse_whitespace(); | ||||
| 
 | ||||
|     while (true) { | ||||
|         m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() }); | ||||
|         parse_whitespace(); | ||||
|         if (!match_number()) | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| float PathDataParser::parse_coordinate() | ||||
| { | ||||
|     return parse_number(); | ||||
| } | ||||
| 
 | ||||
| Vector<float> PathDataParser::parse_coordinate_pair() | ||||
| { | ||||
|     Vector<float> coordinates; | ||||
|     coordinates.append(parse_coordinate()); | ||||
|     if (match_comma_whitespace()) | ||||
|         parse_comma_whitespace(); | ||||
|     coordinates.append(parse_coordinate()); | ||||
|     return coordinates; | ||||
| } | ||||
| 
 | ||||
| Vector<float> PathDataParser::parse_coordinate_sequence() | ||||
| { | ||||
|     Vector<float> sequence; | ||||
|     while (true) { | ||||
|         sequence.append(parse_coordinate()); | ||||
|         if (match_comma_whitespace()) | ||||
|             parse_comma_whitespace(); | ||||
|         if (!match_comma_whitespace() && !match_number()) | ||||
|             break; | ||||
|     } | ||||
|     return sequence; | ||||
| } | ||||
| 
 | ||||
| Vector<Vector<float>> PathDataParser::parse_coordinate_pair_sequence() | ||||
| { | ||||
|     Vector<Vector<float>> sequence; | ||||
|     while (true) { | ||||
|         sequence.append(parse_coordinate_pair()); | ||||
|         if (match_comma_whitespace()) | ||||
|             parse_comma_whitespace(); | ||||
|         if (!match_comma_whitespace() && !match_number()) | ||||
|             break; | ||||
|     } | ||||
|     return sequence; | ||||
| } | ||||
| 
 | ||||
| Vector<float> PathDataParser::parse_coordinate_pair_double() | ||||
| { | ||||
|     Vector<float> coordinates; | ||||
|     coordinates.append(parse_coordinate_pair()); | ||||
|     if (match_comma_whitespace()) | ||||
|         parse_comma_whitespace(); | ||||
|     coordinates.append(parse_coordinate_pair()); | ||||
|     return coordinates; | ||||
| } | ||||
| 
 | ||||
| Vector<float> PathDataParser::parse_coordinate_pair_triplet() | ||||
| { | ||||
|     Vector<float> coordinates; | ||||
|     coordinates.append(parse_coordinate_pair()); | ||||
|     if (match_comma_whitespace()) | ||||
|         parse_comma_whitespace(); | ||||
|     coordinates.append(parse_coordinate_pair()); | ||||
|     if (match_comma_whitespace()) | ||||
|         parse_comma_whitespace(); | ||||
|     coordinates.append(parse_coordinate_pair()); | ||||
|     return coordinates; | ||||
| } | ||||
| 
 | ||||
| Vector<float> PathDataParser::parse_elliptical_arg_argument() | ||||
| { | ||||
|     Vector<float> numbers; | ||||
|     numbers.append(parse_number()); | ||||
|     if (match_comma_whitespace()) | ||||
|         parse_comma_whitespace(); | ||||
|     numbers.append(parse_number()); | ||||
|     if (match_comma_whitespace()) | ||||
|         parse_comma_whitespace(); | ||||
|     numbers.append(parse_number()); | ||||
|     parse_comma_whitespace(); | ||||
|     numbers.append(parse_flag()); | ||||
|     if (match_comma_whitespace()) | ||||
|         parse_comma_whitespace(); | ||||
|     numbers.append(parse_flag()); | ||||
|     if (match_comma_whitespace()) | ||||
|         parse_comma_whitespace(); | ||||
|     numbers.append(parse_coordinate_pair()); | ||||
| 
 | ||||
|     return numbers; | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_whitespace(bool must_match_once) | ||||
| { | ||||
|     bool matched = false; | ||||
|     while (!done() && match_whitespace()) { | ||||
|         consume(); | ||||
|         matched = true; | ||||
|     } | ||||
| 
 | ||||
|         ASSERT(!must_match_once || matched); | ||||
| } | ||||
| 
 | ||||
| void PathDataParser::parse_comma_whitespace() | ||||
| { | ||||
|     if (match(',')) { | ||||
|         consume(); | ||||
|         parse_whitespace(); | ||||
|     } else { | ||||
|         parse_whitespace(1); | ||||
|         if (match(',')) | ||||
|             consume(); | ||||
|         parse_whitespace(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| float PathDataParser::parse_fractional_constant() | ||||
| { | ||||
|     StringBuilder builder; | ||||
|     bool floating_point = false; | ||||
| 
 | ||||
|     while (!done() && isdigit(ch())) | ||||
|         builder.append(consume()); | ||||
| 
 | ||||
|     if (match('.')) { | ||||
|         floating_point = true; | ||||
|         builder.append('.'); | ||||
|         consume(); | ||||
|         while (!done() && isdigit(ch())) | ||||
|             builder.append(consume()); | ||||
|     } else { | ||||
|         ASSERT(builder.length() > 0); | ||||
|     } | ||||
| 
 | ||||
|     if (floating_point) | ||||
|         return strtof(builder.to_string().characters(), nullptr); | ||||
|     return builder.to_string().to_int().value(); | ||||
| } | ||||
| 
 | ||||
| float PathDataParser::parse_number() | ||||
| { | ||||
|     bool negative = false; | ||||
|     if (match('-')) { | ||||
|         consume(); | ||||
|         negative = true; | ||||
|     } else if (match('+')) { | ||||
|         consume(); | ||||
|     } | ||||
|     auto number = parse_fractional_constant(); | ||||
|     if (match('e') || match('E')) | ||||
|         TODO(); | ||||
|     return negative ? number * -1 : number; | ||||
| } | ||||
| 
 | ||||
| float PathDataParser::parse_flag() | ||||
| { | ||||
|     auto number = parse_number(); | ||||
|         ASSERT(number == 0 || number == 1); | ||||
|     return number; | ||||
| } | ||||
| 
 | ||||
| bool PathDataParser::match_whitespace() const | ||||
| { | ||||
|     if (done()) | ||||
|         return false; | ||||
|     char c = ch(); | ||||
|     return c == 0x9 || c == 0x20 || c == 0xa || c == 0xc || c == 0xd; | ||||
| } | ||||
| 
 | ||||
| bool PathDataParser::match_comma_whitespace() const | ||||
| { | ||||
|     return match_whitespace() || match(','); | ||||
| } | ||||
| 
 | ||||
| bool PathDataParser::match_number() const | ||||
| { | ||||
|     return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+'); | ||||
| } | ||||
| 
 | ||||
| HTMLPathElement::HTMLPathElement(Document& document, const FlyString& tag_name) | ||||
|     : HTMLElement(document, tag_name) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| #ifdef PATH_DEBUG | ||||
| static void print_instruction(const PathInstruction& instruction) | ||||
| { | ||||
|     auto& data = instruction.data; | ||||
| 
 | ||||
|     switch (instruction.type) { | ||||
|     case PathInstructionType::Move: | ||||
|         dbg() << "Move (absolute: " << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); i += 2) | ||||
|             dbg() << "    x=" << data[i] << ", y=" << data[i + 1]; | ||||
|         break; | ||||
|     case PathInstructionType::ClosePath: | ||||
|         dbg() << "ClosePath (absolute=" << instruction.absolute << ")"; | ||||
|         break; | ||||
|     case PathInstructionType::Line: | ||||
|         dbg() << "Line (absolute=" << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); i += 2) | ||||
|             dbg() << "    x=" << data[i] << ", y=" << data[i + 1]; | ||||
|         break; | ||||
|     case PathInstructionType::HorizontalLine: | ||||
|         dbg() << "HorizontalLine (absolute=" << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); ++i) | ||||
|             dbg() << "    x=" << data[i]; | ||||
|         break; | ||||
|     case PathInstructionType::VerticalLine: | ||||
|         dbg() << "VerticalLine (absolute=" << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); ++i) | ||||
|             dbg() << "    y=" << data[i]; | ||||
|         break; | ||||
|     case PathInstructionType::Curve: | ||||
|         dbg() << "Curve (absolute=" << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); i += 6) | ||||
|             dbg() << "    (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x2=" << data[i + 2] << ", y2=" << data[i + 3] << "), (x=" << data[i + 4] << ", y=" << data[i + 5] << ")"; | ||||
|         break; | ||||
|     case PathInstructionType::SmoothCurve: | ||||
|         dbg() << "SmoothCurve (absolute: " << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); i += 4) | ||||
|             dbg() << "    (x2=" << data[i] << ", y2=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")"; | ||||
|         break; | ||||
|     case PathInstructionType::QuadraticBezierCurve: | ||||
|         dbg() << "QuadraticBezierCurve (absolute: " << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); i += 4) | ||||
|             dbg() << "    (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")"; | ||||
|         break; | ||||
|     case PathInstructionType::SmoothQuadraticBezierCurve: | ||||
|         dbg() << "SmoothQuadraticBezierCurve (absolute: " << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); i += 2) | ||||
|             dbg() << "    x=" << data[i] << ", y=" << data[i + 1]; | ||||
|         break; | ||||
|     case PathInstructionType::EllipticalArc: | ||||
|         dbg() << "EllipticalArc (absolute: " << instruction.absolute << ")"; | ||||
|         for (size_t i = 0; i < data.size(); i += 7) | ||||
|             dbg() << "    (rx=" << data[i] << ", ry=" << data[i + 1] << ") x-axis-rotation=" << data[i + 2] << ", large-arc-flag=" << data[i + 3] << ", sweep-flag=" << data[i + 4] << ", (x=" << data[i + 5] << ", y=" << data[i + 6] << ")"; | ||||
|         break; | ||||
|     case PathInstructionType::Invalid: | ||||
|         dbg() << "Invalid (absolute: " << instruction.absolute << ")"; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| void HTMLPathElement::parse_attribute(const FlyString& name, const String& value) | ||||
| { | ||||
|     HTMLElement::parse_attribute(name, value); | ||||
|     if (name == "d") | ||||
|         m_instructions = PathDataParser(value).parse(); | ||||
| } | ||||
| 
 | ||||
| void HTMLPathElement::paint(const SvgPaintingContext& context, Gfx::Painter& painter) | ||||
| { | ||||
|     Gfx::Path path; | ||||
| 
 | ||||
|     for (auto& instruction : m_instructions) { | ||||
|         auto& absolute = instruction.absolute; | ||||
|         auto& data = instruction.data; | ||||
| 
 | ||||
| #ifdef PATH_DEBUG | ||||
|         print_instruction(instruction); | ||||
| #endif | ||||
| 
 | ||||
|         switch (instruction.type) { | ||||
|         case PathInstructionType::Move: | ||||
|             if (absolute) { | ||||
|                 path.move_to({ data[0], data[1] }); | ||||
|             } else { | ||||
|                 ASSERT(!path.segments().is_empty()); | ||||
|                 path.move_to(Gfx::FloatPoint { data[0], data[1] } + path.segments().last().point); | ||||
|             } | ||||
|             break; | ||||
|         case PathInstructionType::ClosePath: | ||||
|             path.close(); | ||||
|             break; | ||||
|         case PathInstructionType::Line: | ||||
|             if (absolute) { | ||||
|                 path.line_to({ data[0], data[1] }); | ||||
|             } else { | ||||
|                 ASSERT(!path.segments().is_empty()); | ||||
|                 path.line_to(Gfx::FloatPoint { data[0], data[1] } + path.segments().last().point); | ||||
|             } | ||||
|             break; | ||||
|         case PathInstructionType::HorizontalLine: { | ||||
|             ASSERT(!path.segments().is_empty()); | ||||
|             auto last_point = path.segments().last().point; | ||||
|             if (absolute) { | ||||
|                 path.line_to(Gfx::FloatPoint { data[0], last_point.y() }); | ||||
|             } else { | ||||
|                 path.line_to(Gfx::FloatPoint { data[0] + last_point.x(), last_point.y() }); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case PathInstructionType::VerticalLine: { | ||||
|             ASSERT(!path.segments().is_empty()); | ||||
|             auto last_point = path.segments().last().point; | ||||
|             if (absolute) { | ||||
|                 path.line_to(Gfx::FloatPoint{ last_point.x(), data[0] }); | ||||
|             } else { | ||||
|                 path.line_to(Gfx::FloatPoint{ last_point.x(), data[0] + last_point.y() }); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         case PathInstructionType::QuadraticBezierCurve: | ||||
|             if (absolute) { | ||||
|                 path.quadratic_bezier_curve_to({ data[0], data[1] }, { data[2], data[3] }); | ||||
|             } else { | ||||
|                 ASSERT(!path.segments().is_empty()); | ||||
|                 auto last_point = path.segments().last().point; | ||||
|                 path.quadratic_bezier_curve_to({ data[0] + last_point.x(), data[1] + last_point.y() }, { data[2] + last_point.x(), data[3] + last_point.y() }); | ||||
|             } | ||||
|             break; | ||||
|         case PathInstructionType::Curve: | ||||
|         case PathInstructionType::SmoothCurve: | ||||
|         case PathInstructionType::SmoothQuadraticBezierCurve: | ||||
|         case PathInstructionType::EllipticalArc: | ||||
|             TODO(); | ||||
|         case PathInstructionType::Invalid: | ||||
|             ASSERT_NOT_REACHED(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     painter.fill_path(path, context.fill_color, Gfx::Painter::WindingRule::EvenOdd); | ||||
|     painter.stroke_path(path, context.stroke_color, context.stroke_width); | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										123
									
								
								Libraries/LibWeb/DOM/HTMLPathElement.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								Libraries/LibWeb/DOM/HTMLPathElement.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||
|  * 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 <LibGfx/Bitmap.h> | ||||
| #include <LibWeb/DOM/HTMLElement.h> | ||||
| #include <LibWeb/DOM/SvgContext.h> | ||||
| 
 | ||||
| namespace Web { | ||||
| 
 | ||||
| enum class PathInstructionType { | ||||
|     Move, | ||||
|     ClosePath, | ||||
|     Line, | ||||
|     HorizontalLine, | ||||
|     VerticalLine, | ||||
|     Curve, | ||||
|     SmoothCurve, | ||||
|     QuadraticBezierCurve, | ||||
|     SmoothQuadraticBezierCurve, | ||||
|     EllipticalArc, | ||||
|     Invalid, | ||||
| }; | ||||
| 
 | ||||
| struct PathInstruction { | ||||
|     PathInstructionType type; | ||||
|     bool absolute; | ||||
|     Vector<float> data; | ||||
| }; | ||||
| 
 | ||||
| class PathDataParser final { | ||||
| public: | ||||
|     PathDataParser(const String& source); | ||||
|     ~PathDataParser() = default; | ||||
| 
 | ||||
|     Vector<PathInstruction> parse(); | ||||
| 
 | ||||
| private: | ||||
|     void parse_drawto(); | ||||
| 
 | ||||
|     void parse_moveto(); | ||||
|     void parse_closepath(); | ||||
|     void parse_lineto(); | ||||
|     void parse_horizontal_lineto(); | ||||
|     void parse_vertical_lineto(); | ||||
|     void parse_curveto(); | ||||
|     void parse_smooth_curveto(); | ||||
|     void parse_quadratic_bezier_curveto(); | ||||
|     void parse_smooth_quadratic_bezier_curveto(); | ||||
|     void parse_elliptical_arc(); | ||||
| 
 | ||||
|     float parse_coordinate(); | ||||
|     Vector<float> parse_coordinate_pair(); | ||||
|     Vector<float> parse_coordinate_sequence(); | ||||
|     Vector<Vector<float>> parse_coordinate_pair_sequence(); | ||||
|     Vector<float> parse_coordinate_pair_double(); | ||||
|     Vector<float> parse_coordinate_pair_triplet(); | ||||
|     Vector<float> parse_elliptical_arg_argument(); | ||||
|     void parse_whitespace(bool must_match_once = false); | ||||
|     void parse_comma_whitespace(); | ||||
|     float parse_fractional_constant(); | ||||
|     float parse_number(); | ||||
|     float parse_flag(); | ||||
| 
 | ||||
|     bool match_whitespace() const; | ||||
|     bool match_comma_whitespace() const; | ||||
|     bool match_number() const; | ||||
|     bool match(char c) const { return !done() && ch() == c; } | ||||
| 
 | ||||
|     bool done() const { return m_cursor >= m_source.length(); } | ||||
|     char ch() const { return m_source[m_cursor]; } | ||||
|     char consume() { return m_source[m_cursor++]; } | ||||
| 
 | ||||
|     String m_source; | ||||
|     size_t m_cursor { 0 }; | ||||
|     Vector<PathInstruction> m_instructions; | ||||
| }; | ||||
| 
 | ||||
| class HTMLPathElement final | ||||
|     : public HTMLElement | ||||
|     , public SvgGraphicElement { | ||||
| public: | ||||
|     HTMLPathElement(Document&, const FlyString& tag_name); | ||||
|     virtual ~HTMLPathElement() override = default; | ||||
| 
 | ||||
|     virtual void parse_attribute(const FlyString& name, const String& value) override; | ||||
|     virtual void paint(const SvgPaintingContext&, Gfx::Painter& painter) override; | ||||
| 
 | ||||
| private: | ||||
|     Vector<PathInstruction> m_instructions; | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| inline bool is<HTMLPathElement>(const Node& node) | ||||
| { | ||||
|     return is<Element>(node) && to<Element>(node).tag_name() == HTML::TagNames::path; | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										151
									
								
								Libraries/LibWeb/DOM/HTMLSvgElement.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								Libraries/LibWeb/DOM/HTMLSvgElement.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,151 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||
|  * 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 <LibGfx/Path.h> | ||||
| #include <LibWeb/CSS/StyleResolver.h> | ||||
| #include <LibWeb/DOM/Document.h> | ||||
| #include <LibWeb/DOM/Event.h> | ||||
| #include <LibWeb/DOM/HTMLPathElement.h> | ||||
| #include <LibWeb/DOM/HTMLSvgElement.h> | ||||
| #include <LibWeb/Layout/LayoutSvg.h> | ||||
| #include <ctype.h> | ||||
| 
 | ||||
| namespace Web { | ||||
| 
 | ||||
| static constexpr auto max_svg_area = 16384 * 16384; | ||||
| 
 | ||||
| HTMLSvgElement::HTMLSvgElement(Document& document, const FlyString& tag_name) | ||||
|     : HTMLElement(document, tag_name) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void HTMLSvgElement::parse_attribute(const FlyString& name, const String& value) | ||||
| { | ||||
|     HTMLElement::parse_attribute(name, value); | ||||
|     if (name == "stroke") { | ||||
|         m_stroke_color = Gfx::Color::from_string(value); | ||||
|     } else if (name == "stroke-width") { | ||||
|         auto result = value.to_int(); | ||||
|         if (result.has_value()) | ||||
|             m_stroke_width = result.value(); | ||||
|     } else if (name == "fill") { | ||||
|         m_fill_color = Gfx::Color::from_string(value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| RefPtr<LayoutNode> HTMLSvgElement::create_layout_node(const StyleProperties* parent_style) | ||||
| { | ||||
|     auto style = document().style_resolver().resolve_style(*this, parent_style); | ||||
|     if (style->display() == CSS::Display::None) | ||||
|         return nullptr; | ||||
|     return adopt(*new LayoutSvg(document(), *this, move(style))); | ||||
| } | ||||
| 
 | ||||
| static Gfx::IntSize bitmap_size_for_canvas(const HTMLSvgElement& canvas) | ||||
| { | ||||
|     auto width = canvas.width(); | ||||
|     auto height = canvas.height(); | ||||
| 
 | ||||
|     Checked<size_t> area = width; | ||||
|     area *= height; | ||||
| 
 | ||||
|     if (area.has_overflow()) { | ||||
|         dbg() << "Refusing to create " << width << "x" << height << " svg (overflow)"; | ||||
|         return {}; | ||||
|     } | ||||
|     if (area.value() > max_svg_area) { | ||||
|         dbg() << "Refusing to create " << width << "x" << height << " svg (exceeds maximum size)"; | ||||
|         return {}; | ||||
|     } | ||||
|     return Gfx::IntSize(width, height); | ||||
| } | ||||
| 
 | ||||
| bool HTMLSvgElement::create_bitmap() | ||||
| { | ||||
|     auto size = bitmap_size_for_canvas(*this); | ||||
|     if (size.is_empty()) { | ||||
|         m_bitmap = nullptr; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (!m_bitmap || m_bitmap->size() != size) | ||||
|         m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, size); | ||||
| 
 | ||||
|     Gfx::Painter painter(*m_bitmap); | ||||
|     paint(painter); | ||||
| 
 | ||||
|     return m_bitmap; | ||||
| } | ||||
| 
 | ||||
| SvgPaintingContext HTMLSvgElement::make_context() const | ||||
| { | ||||
|     SvgPaintingContext context; | ||||
| 
 | ||||
|     if (m_stroke_width.has_value()) | ||||
|         context.stroke_width = m_stroke_width.value(); | ||||
|     if (m_stroke_color.has_value()) | ||||
|         context.stroke_color = m_stroke_color.value(); | ||||
|     if (m_fill_color.has_value()) | ||||
|         context.fill_color = m_fill_color.value(); | ||||
| 
 | ||||
|     return context; | ||||
| } | ||||
| 
 | ||||
| unsigned HTMLSvgElement::width() const | ||||
| { | ||||
|     return attribute(HTML::AttributeNames::width).to_uint().value_or(300); | ||||
| } | ||||
| 
 | ||||
| unsigned HTMLSvgElement::height() const | ||||
| { | ||||
|     return attribute(HTML::AttributeNames::height).to_uint().value_or(150); | ||||
| } | ||||
| 
 | ||||
| static bool is_svg_graphic_element(const HTMLElement& element) | ||||
| { | ||||
|     return is<HTMLPathElement>(element); | ||||
| } | ||||
| 
 | ||||
| static SvgGraphicElement& as_svg_graphic_element(HTMLElement& element) | ||||
| { | ||||
|     if (is<HTMLPathElement>(element)) | ||||
|         return to<HTMLPathElement>(element); | ||||
|     ASSERT_NOT_REACHED(); | ||||
| } | ||||
| 
 | ||||
| void HTMLSvgElement::paint(Gfx::Painter& painter) | ||||
| { | ||||
|     for_each_child([&](Node& child) { | ||||
|         if (is<HTMLElement>(child)) { | ||||
|             auto& element = to<HTMLElement>(child); | ||||
|             if (is_svg_graphic_element(element)) { | ||||
|                 as_svg_graphic_element(element).paint(make_context(), painter); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										66
									
								
								Libraries/LibWeb/DOM/HTMLSvgElement.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								Libraries/LibWeb/DOM/HTMLSvgElement.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||
|  * 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 <LibGfx/Bitmap.h> | ||||
| #include <LibWeb/DOM/HTMLElement.h> | ||||
| #include <LibWeb/DOM/SvgContext.h> | ||||
| 
 | ||||
| namespace Web { | ||||
| 
 | ||||
| class HTMLSvgElement final : public HTMLElement { | ||||
| public: | ||||
|     HTMLSvgElement(Document&, const FlyString& tag_name); | ||||
|     virtual ~HTMLSvgElement() override = default; | ||||
| 
 | ||||
|     virtual void parse_attribute(const FlyString& name, const String& value) override; | ||||
|     virtual RefPtr<LayoutNode> create_layout_node(const StyleProperties* parent_style) override; | ||||
| 
 | ||||
|     const RefPtr<Gfx::Bitmap> bitmap() const { return m_bitmap; } | ||||
|     bool create_bitmap(); | ||||
|     SvgPaintingContext make_context() const; | ||||
| 
 | ||||
|     unsigned width() const; | ||||
|     unsigned height() const; | ||||
| 
 | ||||
| private: | ||||
|     void paint(Gfx::Painter&); | ||||
| 
 | ||||
|     RefPtr<Gfx::Bitmap> m_bitmap; | ||||
| 
 | ||||
|     Optional<float> m_stroke_width; | ||||
|     Optional<Color> m_stroke_color; | ||||
|     Optional<Color> m_fill_color; | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| inline bool is<HTMLSvgElement>(const Node& node) | ||||
| { | ||||
|     return is<Element>(node) && to<Element>(node).tag_name() == HTML::TagNames::svg; | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										45
									
								
								Libraries/LibWeb/DOM/SvgContext.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Libraries/LibWeb/DOM/SvgContext.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||
|  * 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 <LibGfx/Color.h> | ||||
| #include <LibGfx/Painter.h> | ||||
| 
 | ||||
| namespace Web { | ||||
| 
 | ||||
| struct SvgPaintingContext { | ||||
|     Gfx::Color fill_color { Gfx::Color::Black }; | ||||
|     Gfx::Color stroke_color { Gfx::Color::Black }; | ||||
|     float stroke_width { 1 }; | ||||
| }; | ||||
| 
 | ||||
| class SvgGraphicElement { | ||||
| public: | ||||
|     virtual void paint(const SvgPaintingContext&, Gfx::Painter& painter) = 0; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | @ -110,6 +110,7 @@ void initialize(); | |||
|     __ENUMERATE_HTML_TAG(option)     \ | ||||
|     __ENUMERATE_HTML_TAG(p)          \ | ||||
|     __ENUMERATE_HTML_TAG(param)      \ | ||||
|     __ENUMERATE_HTML_TAG(path)       \ | ||||
|     __ENUMERATE_HTML_TAG(plaintext)  \ | ||||
|     __ENUMERATE_HTML_TAG(pre)        \ | ||||
|     __ENUMERATE_HTML_TAG(ruby)       \ | ||||
|  |  | |||
							
								
								
									
										67
									
								
								Libraries/LibWeb/Layout/LayoutSvg.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								Libraries/LibWeb/Layout/LayoutSvg.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||
|  * 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 <LibGUI/Painter.h> | ||||
| #include <LibGfx/Font.h> | ||||
| #include <LibGfx/StylePainter.h> | ||||
| #include <LibWeb/Layout/LayoutSvg.h> | ||||
| 
 | ||||
| namespace Web { | ||||
| 
 | ||||
| LayoutSvg::LayoutSvg(Document& document, const HTMLSvgElement& element, NonnullRefPtr<StyleProperties> style) | ||||
|     : LayoutReplaced(document, element, move(style)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void LayoutSvg::layout(LayoutMode layout_mode) | ||||
| { | ||||
|     set_has_intrinsic_width(true); | ||||
|     set_has_intrinsic_height(true); | ||||
|     set_intrinsic_width(node().width()); | ||||
|     set_intrinsic_height(node().height()); | ||||
|     LayoutReplaced::layout(layout_mode); | ||||
| } | ||||
| 
 | ||||
| void LayoutSvg::paint(PaintContext& context, PaintPhase phase) | ||||
| { | ||||
|     if (!is_visible()) | ||||
|         return; | ||||
| 
 | ||||
|     LayoutReplaced::paint(context, phase); | ||||
| 
 | ||||
|     if (phase == PaintPhase::Foreground) { | ||||
|         if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect()))) | ||||
|             return; | ||||
| 
 | ||||
|         if (!node().bitmap()) | ||||
|             node().create_bitmap(); | ||||
| 
 | ||||
|         ASSERT(node().bitmap()); | ||||
|         context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *node().bitmap(), node().bitmap()->rect()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										50
									
								
								Libraries/LibWeb/Layout/LayoutSvg.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								Libraries/LibWeb/Layout/LayoutSvg.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||
|  * 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 <LibWeb/DOM/HTMLSvgElement.h> | ||||
| #include <LibWeb/Layout/LayoutReplaced.h> | ||||
| 
 | ||||
| namespace Web { | ||||
| 
 | ||||
| class HTMLSvgElement; | ||||
| 
 | ||||
| class LayoutSvg : public LayoutReplaced { | ||||
| public: | ||||
|     LayoutSvg(Document&, const HTMLSvgElement&, NonnullRefPtr<StyleProperties>); | ||||
|     virtual ~LayoutSvg() override = default; | ||||
|     virtual void layout(LayoutMode = LayoutMode::Default) override; | ||||
|     virtual void paint(PaintContext&, PaintPhase) override; | ||||
| 
 | ||||
|     HTMLSvgElement& node() { return static_cast<HTMLSvgElement&>(LayoutReplaced::node()); } | ||||
| 
 | ||||
| private: | ||||
|     virtual const char* class_name() const override { return "LayoutSvg"; } | ||||
|     virtual bool is_canvas() const override { return true; } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Matthew Olsson
						Matthew Olsson