SVGPathElement.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /*
  2. * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include <AK/Debug.h>
  27. #include <AK/StringBuilder.h>
  28. #include <LibGfx/Painter.h>
  29. #include <LibGfx/Path.h>
  30. #include <LibWeb/DOM/Document.h>
  31. #include <LibWeb/DOM/Event.h>
  32. #include <LibWeb/Layout/SVGPathBox.h>
  33. #include <LibWeb/SVG/SVGPathElement.h>
  34. #include <ctype.h>
  35. namespace Web::SVG {
  36. #ifdef PATH_DEBUG
  37. static void print_instruction(const PathInstruction& instruction)
  38. {
  39. auto& data = instruction.data;
  40. switch (instruction.type) {
  41. case PathInstructionType::Move:
  42. dbg() << "Move (absolute=" << instruction.absolute << ")";
  43. for (size_t i = 0; i < data.size(); i += 2)
  44. dbg() << " x=" << data[i] << ", y=" << data[i + 1];
  45. break;
  46. case PathInstructionType::ClosePath:
  47. dbg() << "ClosePath (absolute=" << instruction.absolute << ")";
  48. break;
  49. case PathInstructionType::Line:
  50. dbg() << "Line (absolute=" << instruction.absolute << ")";
  51. for (size_t i = 0; i < data.size(); i += 2)
  52. dbg() << " x=" << data[i] << ", y=" << data[i + 1];
  53. break;
  54. case PathInstructionType::HorizontalLine:
  55. dbg() << "HorizontalLine (absolute=" << instruction.absolute << ")";
  56. for (size_t i = 0; i < data.size(); ++i)
  57. dbg() << " x=" << data[i];
  58. break;
  59. case PathInstructionType::VerticalLine:
  60. dbg() << "VerticalLine (absolute=" << instruction.absolute << ")";
  61. for (size_t i = 0; i < data.size(); ++i)
  62. dbg() << " y=" << data[i];
  63. break;
  64. case PathInstructionType::Curve:
  65. dbg() << "Curve (absolute=" << instruction.absolute << ")";
  66. for (size_t i = 0; i < data.size(); i += 6)
  67. dbg() << " (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x2=" << data[i + 2] << ", y2=" << data[i + 3] << "), (x=" << data[i + 4] << ", y=" << data[i + 5] << ")";
  68. break;
  69. case PathInstructionType::SmoothCurve:
  70. dbg() << "SmoothCurve (absolute=" << instruction.absolute << ")";
  71. for (size_t i = 0; i < data.size(); i += 4)
  72. dbg() << " (x2=" << data[i] << ", y2=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")";
  73. break;
  74. case PathInstructionType::QuadraticBezierCurve:
  75. dbg() << "QuadraticBezierCurve (absolute=" << instruction.absolute << ")";
  76. for (size_t i = 0; i < data.size(); i += 4)
  77. dbg() << " (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")";
  78. break;
  79. case PathInstructionType::SmoothQuadraticBezierCurve:
  80. dbg() << "SmoothQuadraticBezierCurve (absolute=" << instruction.absolute << ")";
  81. for (size_t i = 0; i < data.size(); i += 2)
  82. dbg() << " x=" << data[i] << ", y=" << data[i + 1];
  83. break;
  84. case PathInstructionType::EllipticalArc:
  85. dbg() << "EllipticalArc (absolute=" << instruction.absolute << ")";
  86. for (size_t i = 0; i < data.size(); i += 7)
  87. 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] << ")";
  88. break;
  89. case PathInstructionType::Invalid:
  90. dbgln("Invalid");
  91. break;
  92. }
  93. }
  94. #endif
  95. PathDataParser::PathDataParser(const String& source)
  96. : m_source(source)
  97. {
  98. }
  99. Vector<PathInstruction> PathDataParser::parse()
  100. {
  101. parse_whitespace();
  102. while (!done())
  103. parse_drawto();
  104. if (!m_instructions.is_empty() && m_instructions[0].type != PathInstructionType::Move)
  105. ASSERT_NOT_REACHED();
  106. return m_instructions;
  107. }
  108. void PathDataParser::parse_drawto()
  109. {
  110. if (match('M') || match('m')) {
  111. parse_moveto();
  112. } else if (match('Z') || match('z')) {
  113. parse_closepath();
  114. } else if (match('L') || match('l')) {
  115. parse_lineto();
  116. } else if (match('H') || match('h')) {
  117. parse_horizontal_lineto();
  118. } else if (match('V') || match('v')) {
  119. parse_vertical_lineto();
  120. } else if (match('C') || match('c')) {
  121. parse_curveto();
  122. } else if (match('S') || match('s')) {
  123. parse_smooth_curveto();
  124. } else if (match('Q') || match('q')) {
  125. parse_quadratic_bezier_curveto();
  126. } else if (match('T') || match('t')) {
  127. parse_smooth_quadratic_bezier_curveto();
  128. } else if (match('A') || match('a')) {
  129. parse_elliptical_arc();
  130. } else {
  131. dbgln("PathDataParser::parse_drawto failed to match: '{}'", ch());
  132. TODO();
  133. }
  134. }
  135. void PathDataParser::parse_moveto()
  136. {
  137. bool absolute = consume() == 'M';
  138. parse_whitespace();
  139. for (auto& pair : parse_coordinate_pair_sequence())
  140. m_instructions.append({ PathInstructionType::Move, absolute, pair });
  141. }
  142. void PathDataParser::parse_closepath()
  143. {
  144. bool absolute = consume() == 'Z';
  145. parse_whitespace();
  146. m_instructions.append({ PathInstructionType::ClosePath, absolute, {} });
  147. }
  148. void PathDataParser::parse_lineto()
  149. {
  150. bool absolute = consume() == 'L';
  151. parse_whitespace();
  152. for (auto& pair : parse_coordinate_pair_sequence())
  153. m_instructions.append({ PathInstructionType::Line, absolute, pair });
  154. }
  155. void PathDataParser::parse_horizontal_lineto()
  156. {
  157. bool absolute = consume() == 'H';
  158. parse_whitespace();
  159. m_instructions.append({ PathInstructionType::HorizontalLine, absolute, parse_coordinate_sequence() });
  160. }
  161. void PathDataParser::parse_vertical_lineto()
  162. {
  163. bool absolute = consume() == 'V';
  164. parse_whitespace();
  165. m_instructions.append({ PathInstructionType::VerticalLine, absolute, parse_coordinate_sequence() });
  166. }
  167. void PathDataParser::parse_curveto()
  168. {
  169. bool absolute = consume() == 'C';
  170. parse_whitespace();
  171. while (true) {
  172. m_instructions.append({ PathInstructionType::Curve, absolute, parse_coordinate_pair_triplet() });
  173. if (match_comma_whitespace())
  174. parse_comma_whitespace();
  175. if (!match_coordinate())
  176. break;
  177. }
  178. }
  179. void PathDataParser::parse_smooth_curveto()
  180. {
  181. bool absolute = consume() == 'S';
  182. parse_whitespace();
  183. while (true) {
  184. m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() });
  185. if (match_comma_whitespace())
  186. parse_comma_whitespace();
  187. if (!match_coordinate())
  188. break;
  189. }
  190. }
  191. void PathDataParser::parse_quadratic_bezier_curveto()
  192. {
  193. bool absolute = consume() == 'Q';
  194. parse_whitespace();
  195. while (true) {
  196. m_instructions.append({ PathInstructionType::QuadraticBezierCurve, absolute, parse_coordinate_pair_double() });
  197. if (match_comma_whitespace())
  198. parse_comma_whitespace();
  199. if (!match_coordinate())
  200. break;
  201. }
  202. }
  203. void PathDataParser::parse_smooth_quadratic_bezier_curveto()
  204. {
  205. bool absolute = consume() == 'T';
  206. parse_whitespace();
  207. while (true) {
  208. m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair() });
  209. if (match_comma_whitespace())
  210. parse_comma_whitespace();
  211. if (!match_coordinate())
  212. break;
  213. }
  214. }
  215. void PathDataParser::parse_elliptical_arc()
  216. {
  217. bool absolute = consume() == 'A';
  218. parse_whitespace();
  219. while (true) {
  220. m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() });
  221. if (match_comma_whitespace())
  222. parse_comma_whitespace();
  223. if (!match_coordinate())
  224. break;
  225. }
  226. }
  227. float PathDataParser::parse_coordinate()
  228. {
  229. return parse_sign() * parse_number();
  230. }
  231. Vector<float> PathDataParser::parse_coordinate_pair()
  232. {
  233. Vector<float> coordinates;
  234. coordinates.append(parse_coordinate());
  235. if (match_comma_whitespace())
  236. parse_comma_whitespace();
  237. coordinates.append(parse_coordinate());
  238. return coordinates;
  239. }
  240. Vector<float> PathDataParser::parse_coordinate_sequence()
  241. {
  242. Vector<float> sequence;
  243. while (true) {
  244. sequence.append(parse_coordinate());
  245. if (match_comma_whitespace())
  246. parse_comma_whitespace();
  247. if (!match_comma_whitespace() && !match_coordinate())
  248. break;
  249. }
  250. return sequence;
  251. }
  252. Vector<Vector<float>> PathDataParser::parse_coordinate_pair_sequence()
  253. {
  254. Vector<Vector<float>> sequence;
  255. while (true) {
  256. sequence.append(parse_coordinate_pair());
  257. if (match_comma_whitespace())
  258. parse_comma_whitespace();
  259. if (!match_comma_whitespace() && !match_coordinate())
  260. break;
  261. }
  262. return sequence;
  263. }
  264. Vector<float> PathDataParser::parse_coordinate_pair_double()
  265. {
  266. Vector<float> coordinates;
  267. coordinates.append(parse_coordinate_pair());
  268. if (match_comma_whitespace())
  269. parse_comma_whitespace();
  270. coordinates.append(parse_coordinate_pair());
  271. return coordinates;
  272. }
  273. Vector<float> PathDataParser::parse_coordinate_pair_triplet()
  274. {
  275. Vector<float> coordinates;
  276. coordinates.append(parse_coordinate_pair());
  277. if (match_comma_whitespace())
  278. parse_comma_whitespace();
  279. coordinates.append(parse_coordinate_pair());
  280. if (match_comma_whitespace())
  281. parse_comma_whitespace();
  282. coordinates.append(parse_coordinate_pair());
  283. return coordinates;
  284. }
  285. Vector<float> PathDataParser::parse_elliptical_arg_argument()
  286. {
  287. Vector<float> numbers;
  288. numbers.append(parse_number());
  289. if (match_comma_whitespace())
  290. parse_comma_whitespace();
  291. numbers.append(parse_number());
  292. if (match_comma_whitespace())
  293. parse_comma_whitespace();
  294. numbers.append(parse_number());
  295. parse_comma_whitespace();
  296. numbers.append(parse_flag());
  297. if (match_comma_whitespace())
  298. parse_comma_whitespace();
  299. numbers.append(parse_flag());
  300. if (match_comma_whitespace())
  301. parse_comma_whitespace();
  302. numbers.append(parse_coordinate_pair());
  303. return numbers;
  304. }
  305. void PathDataParser::parse_whitespace(bool must_match_once)
  306. {
  307. bool matched = false;
  308. while (!done() && match_whitespace()) {
  309. consume();
  310. matched = true;
  311. }
  312. ASSERT(!must_match_once || matched);
  313. }
  314. void PathDataParser::parse_comma_whitespace()
  315. {
  316. if (match(',')) {
  317. consume();
  318. parse_whitespace();
  319. } else {
  320. parse_whitespace(1);
  321. if (match(','))
  322. consume();
  323. parse_whitespace();
  324. }
  325. }
  326. float PathDataParser::parse_fractional_constant()
  327. {
  328. StringBuilder builder;
  329. bool floating_point = false;
  330. while (!done() && isdigit(ch()))
  331. builder.append(consume());
  332. if (match('.')) {
  333. floating_point = true;
  334. builder.append('.');
  335. consume();
  336. while (!done() && isdigit(ch()))
  337. builder.append(consume());
  338. } else {
  339. ASSERT(builder.length() > 0);
  340. }
  341. if (floating_point)
  342. return strtof(builder.to_string().characters(), nullptr);
  343. return builder.to_string().to_int().value();
  344. }
  345. float PathDataParser::parse_number()
  346. {
  347. auto number = parse_fractional_constant();
  348. if (match('e') || match('E'))
  349. TODO();
  350. return number;
  351. }
  352. float PathDataParser::parse_flag()
  353. {
  354. if (!match('0') && !match('1'))
  355. ASSERT_NOT_REACHED();
  356. return consume() - '0';
  357. }
  358. int PathDataParser::parse_sign()
  359. {
  360. if (match('-')) {
  361. consume();
  362. return -1;
  363. }
  364. if (match('+'))
  365. consume();
  366. return 1;
  367. }
  368. bool PathDataParser::match_whitespace() const
  369. {
  370. if (done())
  371. return false;
  372. char c = ch();
  373. return c == 0x9 || c == 0x20 || c == 0xa || c == 0xc || c == 0xd;
  374. }
  375. bool PathDataParser::match_comma_whitespace() const
  376. {
  377. return match_whitespace() || match(',');
  378. }
  379. bool PathDataParser::match_coordinate() const
  380. {
  381. return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.');
  382. }
  383. SVGPathElement::SVGPathElement(DOM::Document& document, const QualifiedName& qualified_name)
  384. : SVGGeometryElement(document, qualified_name)
  385. {
  386. }
  387. RefPtr<Layout::Node> SVGPathElement::create_layout_node()
  388. {
  389. auto style = document().style_resolver().resolve_style(*this);
  390. if (style->display() == CSS::Display::None)
  391. return nullptr;
  392. return adopt(*new Layout::SVGPathBox(document(), *this, move(style)));
  393. }
  394. void SVGPathElement::parse_attribute(const FlyString& name, const String& value)
  395. {
  396. SVGGeometryElement::parse_attribute(name, value);
  397. if (name == "d")
  398. m_instructions = PathDataParser(value).parse();
  399. }
  400. Gfx::Path& SVGPathElement::get_path()
  401. {
  402. if (m_path.has_value())
  403. return m_path.value();
  404. Gfx::Path path;
  405. for (auto& instruction : m_instructions) {
  406. auto& absolute = instruction.absolute;
  407. auto& data = instruction.data;
  408. #ifdef PATH_DEBUG
  409. print_instruction(instruction);
  410. #endif
  411. bool clear_last_control_point = true;
  412. switch (instruction.type) {
  413. case PathInstructionType::Move: {
  414. Gfx::FloatPoint point = { data[0], data[1] };
  415. if (absolute) {
  416. path.move_to(point);
  417. } else {
  418. ASSERT(!path.segments().is_empty());
  419. path.move_to(point + path.segments().last().point());
  420. }
  421. break;
  422. }
  423. case PathInstructionType::ClosePath:
  424. path.close();
  425. break;
  426. case PathInstructionType::Line: {
  427. Gfx::FloatPoint point = { data[0], data[1] };
  428. if (absolute) {
  429. path.line_to(point);
  430. } else {
  431. ASSERT(!path.segments().is_empty());
  432. path.line_to(point + path.segments().last().point());
  433. }
  434. break;
  435. }
  436. case PathInstructionType::HorizontalLine: {
  437. ASSERT(!path.segments().is_empty());
  438. auto last_point = path.segments().last().point();
  439. if (absolute) {
  440. path.line_to(Gfx::FloatPoint { data[0], last_point.y() });
  441. } else {
  442. path.line_to(Gfx::FloatPoint { data[0] + last_point.x(), last_point.y() });
  443. }
  444. break;
  445. }
  446. case PathInstructionType::VerticalLine: {
  447. ASSERT(!path.segments().is_empty());
  448. auto last_point = path.segments().last().point();
  449. if (absolute) {
  450. path.line_to(Gfx::FloatPoint { last_point.x(), data[0] });
  451. } else {
  452. path.line_to(Gfx::FloatPoint { last_point.x(), data[0] + last_point.y() });
  453. }
  454. break;
  455. }
  456. case PathInstructionType::EllipticalArc: {
  457. double rx = data[0];
  458. double ry = data[1];
  459. double x_axis_rotation = data[2] * M_DEG2RAD;
  460. double large_arc_flag = data[3];
  461. double sweep_flag = data[4];
  462. double x_axis_rotation_c = cos(x_axis_rotation);
  463. double x_axis_rotation_s = sin(x_axis_rotation);
  464. auto& last_point = path.segments().last().point();
  465. Gfx::FloatPoint next_point;
  466. if (absolute) {
  467. next_point = { data[5], data[6] };
  468. } else {
  469. next_point = { data[5] + last_point.x(), data[6] + last_point.y() };
  470. }
  471. // Step 1 of out-of-range radii correction
  472. if (rx == 0.0 || ry == 0.0) {
  473. path.line_to(next_point);
  474. break;
  475. }
  476. // Step 2 of out-of-range radii correction
  477. if (rx < 0)
  478. rx *= -1.0;
  479. if (ry < 0)
  480. ry *= -1.0;
  481. // Find (cx, cy), theta_1, theta_delta
  482. // Step 1: Compute (x1', y1')
  483. auto x_avg = (last_point.x() - next_point.x()) / 2.0f;
  484. auto y_avg = (last_point.y() - next_point.y()) / 2.0f;
  485. auto x1p = x_axis_rotation_c * x_avg + x_axis_rotation_s * y_avg;
  486. auto y1p = -x_axis_rotation_s * x_avg + x_axis_rotation_c * y_avg;
  487. // Step 2: Compute (cx', cy')
  488. double x1p_sq = pow(x1p, 2.0);
  489. double y1p_sq = pow(y1p, 2.0);
  490. double rx_sq = pow(rx, 2.0);
  491. double ry_sq = pow(ry, 2.0);
  492. // Step 3 of out-of-range radii correction
  493. double lambda = x1p_sq / rx_sq + y1p_sq / ry_sq;
  494. double multiplier;
  495. if (lambda > 1.0) {
  496. auto lambda_sqrt = sqrt(lambda);
  497. rx *= lambda_sqrt;
  498. ry *= lambda_sqrt;
  499. multiplier = 0.0;
  500. } else {
  501. double numerator = rx_sq * ry_sq - rx_sq * y1p_sq - ry_sq * x1p_sq;
  502. double denominator = rx_sq * y1p_sq + ry_sq * x1p_sq;
  503. multiplier = sqrt(numerator / denominator);
  504. }
  505. if (large_arc_flag == sweep_flag)
  506. multiplier *= -1.0;
  507. double cxp = multiplier * rx * y1p / ry;
  508. double cyp = multiplier * -ry * x1p / rx;
  509. // Step 3: Compute (cx, cy) from (cx', cy')
  510. x_avg = (last_point.x() + next_point.x()) / 2.0f;
  511. y_avg = (last_point.y() + next_point.y()) / 2.0f;
  512. double cx = x_axis_rotation_c * cxp - x_axis_rotation_s * cyp + x_avg;
  513. double cy = x_axis_rotation_s * cxp + x_axis_rotation_c * cyp + y_avg;
  514. double theta_1 = atan2((y1p - cyp) / ry, (x1p - cxp) / rx);
  515. double theta_2 = atan2((-y1p - cyp) / ry, (-x1p - cxp) / rx);
  516. auto theta_delta = theta_2 - theta_1;
  517. if (sweep_flag == 0 && theta_delta > 0.0f) {
  518. theta_delta -= M_TAU;
  519. } else if (sweep_flag != 0 && theta_delta < 0) {
  520. theta_delta += M_TAU;
  521. }
  522. path.elliptical_arc_to(next_point, { cx, cy }, { rx, ry }, x_axis_rotation, theta_1, theta_delta);
  523. break;
  524. }
  525. case PathInstructionType::QuadraticBezierCurve: {
  526. clear_last_control_point = false;
  527. Gfx::FloatPoint through = { data[0], data[1] };
  528. Gfx::FloatPoint point = { data[2], data[3] };
  529. if (absolute) {
  530. path.quadratic_bezier_curve_to(through, point);
  531. m_previous_control_point = through;
  532. } else {
  533. ASSERT(!path.segments().is_empty());
  534. auto last_point = path.segments().last().point();
  535. auto control_point = through + last_point;
  536. path.quadratic_bezier_curve_to(control_point, point + last_point);
  537. m_previous_control_point = control_point;
  538. }
  539. break;
  540. }
  541. case PathInstructionType::SmoothQuadraticBezierCurve: {
  542. clear_last_control_point = false;
  543. ASSERT(!path.segments().is_empty());
  544. auto last_point = path.segments().last().point();
  545. if (m_previous_control_point.is_null()) {
  546. m_previous_control_point = last_point;
  547. }
  548. auto dx_end_control = last_point.dx_relative_to(m_previous_control_point);
  549. auto dy_end_control = last_point.dy_relative_to(m_previous_control_point);
  550. auto control_point = Gfx::FloatPoint { last_point.x() + dx_end_control, last_point.y() + dy_end_control };
  551. Gfx::FloatPoint end_point = { data[0], data[1] };
  552. if (absolute) {
  553. path.quadratic_bezier_curve_to(control_point, end_point);
  554. } else {
  555. path.quadratic_bezier_curve_to(control_point, end_point + last_point);
  556. }
  557. m_previous_control_point = control_point;
  558. break;
  559. }
  560. case PathInstructionType::Curve:
  561. case PathInstructionType::SmoothCurve:
  562. // Instead of crashing the browser every time we come across an SVG
  563. // with these path instructions, let's just skip them
  564. continue;
  565. case PathInstructionType::Invalid:
  566. ASSERT_NOT_REACHED();
  567. }
  568. if (clear_last_control_point) {
  569. m_previous_control_point = Gfx::FloatPoint {};
  570. }
  571. }
  572. m_path = path;
  573. return m_path.value();
  574. }
  575. }