TinyVGLoader.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. /*
  2. * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Array.h>
  7. #include <AK/Endian.h>
  8. #include <AK/FixedArray.h>
  9. #include <AK/LEB128.h>
  10. #include <AK/MemoryStream.h>
  11. #include <AK/Variant.h>
  12. #include <LibCore/File.h>
  13. #include <LibGfx/AntiAliasingPainter.h>
  14. #include <LibGfx/DeprecatedPainter.h>
  15. #include <LibGfx/ImageFormats/TinyVGLoader.h>
  16. #include <LibGfx/Line.h>
  17. #include <LibGfx/Point.h>
  18. namespace Gfx {
  19. using VarUInt = LEB128<u32>;
  20. static constexpr Array<u8, 2> TVG_MAGIC { 0x72, 0x56 };
  21. enum class ColorEncoding : u8 {
  22. RGBA8888 = 0,
  23. RGB565 = 1,
  24. RGBAF32 = 2,
  25. Custom = 3
  26. };
  27. enum class CoordinateRange : u8 {
  28. Default = 0,
  29. Reduced = 1,
  30. Enhanced = 2
  31. };
  32. enum class StyleType : u8 {
  33. FlatColored = 0,
  34. LinearGradient = 1,
  35. RadialGradinet = 2
  36. };
  37. enum class Command : u8 {
  38. EndOfDocument = 0,
  39. FillPolygon = 1,
  40. FillRectangles = 2,
  41. FillPath = 3,
  42. DrawLines = 4,
  43. DrawLineLoop = 5,
  44. DrawLineStrip = 6,
  45. DrawLinePath = 7,
  46. OutlineFillPolygon = 8,
  47. OutlineFillRectangles = 9,
  48. OutLineFillPath = 10
  49. };
  50. struct FillCommandHeader {
  51. u32 count;
  52. TinyVGDecodedImageData::Style style;
  53. };
  54. struct DrawCommandHeader {
  55. u32 count;
  56. TinyVGDecodedImageData::Style line_style;
  57. float line_width;
  58. };
  59. struct OutlineFillCommandHeader {
  60. u32 count;
  61. TinyVGDecodedImageData::Style fill_style;
  62. TinyVGDecodedImageData::Style line_style;
  63. float line_width;
  64. };
  65. enum class PathCommand : u8 {
  66. Line = 0,
  67. HorizontalLine = 1,
  68. VerticalLine = 2,
  69. CubicBezier = 3,
  70. ArcCircle = 4,
  71. ArcEllipse = 5,
  72. ClosePath = 6,
  73. QuadraticBezier = 7
  74. };
  75. struct TinyVGHeader {
  76. u8 version;
  77. u8 scale;
  78. ColorEncoding color_encoding;
  79. CoordinateRange coordinate_range;
  80. u32 width;
  81. u32 height;
  82. u32 color_count;
  83. };
  84. static ErrorOr<TinyVGHeader> decode_tinyvg_header(Stream& stream)
  85. {
  86. TinyVGHeader header {};
  87. Array<u8, 2> magic_bytes;
  88. TRY(stream.read_until_filled(magic_bytes));
  89. if (magic_bytes != TVG_MAGIC)
  90. return Error::from_string_literal("Invalid TinyVG: Incorrect header magic");
  91. header.version = TRY(stream.read_value<u8>());
  92. u8 properties = TRY(stream.read_value<u8>());
  93. header.scale = properties & 0xF;
  94. header.color_encoding = static_cast<ColorEncoding>((properties >> 4) & 0x3);
  95. header.coordinate_range = static_cast<CoordinateRange>((properties >> 6) & 0x3);
  96. switch (header.coordinate_range) {
  97. case CoordinateRange::Default:
  98. header.width = TRY(stream.read_value<LittleEndian<u16>>());
  99. header.height = TRY(stream.read_value<LittleEndian<u16>>());
  100. break;
  101. case CoordinateRange::Reduced:
  102. header.width = TRY(stream.read_value<u8>());
  103. header.height = TRY(stream.read_value<u8>());
  104. break;
  105. case CoordinateRange::Enhanced:
  106. header.width = TRY(stream.read_value<LittleEndian<u32>>());
  107. header.height = TRY(stream.read_value<LittleEndian<u32>>());
  108. break;
  109. default:
  110. return Error::from_string_literal("Invalid TinyVG: Bad coordinate range");
  111. }
  112. header.color_count = TRY(stream.read_value<VarUInt>());
  113. return header;
  114. }
  115. static ErrorOr<Vector<Color>> decode_color_table(Stream& stream, ColorEncoding encoding, u32 color_count)
  116. {
  117. if (encoding == ColorEncoding::Custom)
  118. return Error::from_string_literal("Invalid TinyVG: Unsupported color encoding");
  119. static constexpr size_t MAX_INITIAL_COLOR_TABLE_SIZE = 65536;
  120. Vector<Color> color_table;
  121. TRY(color_table.try_ensure_capacity(min(MAX_INITIAL_COLOR_TABLE_SIZE, color_count)));
  122. auto parse_color = [&]() -> ErrorOr<Color> {
  123. switch (encoding) {
  124. case ColorEncoding::RGBA8888: {
  125. Array<u8, 4> rgba;
  126. TRY(stream.read_until_filled(rgba));
  127. return Color(rgba[0], rgba[1], rgba[2], rgba[3]);
  128. }
  129. case ColorEncoding::RGB565: {
  130. u16 color = TRY(stream.read_value<LittleEndian<u16>>());
  131. auto red = (color >> (6 + 5)) & 0x1f;
  132. auto green = (color >> 5) & 0x3f;
  133. auto blue = (color >> 0) & 0x1f;
  134. return Color((red * 255 + 15) / 31, (green * 255 + 31) / 63, (blue * 255 + 15) / 31);
  135. }
  136. case ColorEncoding::RGBAF32: {
  137. auto red = TRY(stream.read_value<LittleEndian<f32>>());
  138. auto green = TRY(stream.read_value<LittleEndian<f32>>());
  139. auto blue = TRY(stream.read_value<LittleEndian<f32>>());
  140. auto alpha = TRY(stream.read_value<LittleEndian<f32>>());
  141. return Color(
  142. clamp(red * 255.0f, 0.0f, 255.0f),
  143. clamp(green * 255.0f, 0.0f, 255.0f),
  144. clamp(blue * 255.0f, 0.0f, 255.0f),
  145. clamp(alpha * 255.0f, 0.0f, 255.0f));
  146. }
  147. default:
  148. return Error::from_string_literal("Invalid TinyVG: Bad color encoding");
  149. }
  150. };
  151. while (color_count-- > 0) {
  152. TRY(color_table.try_append(TRY(parse_color())));
  153. }
  154. return color_table;
  155. }
  156. class TinyVGReader {
  157. public:
  158. TinyVGReader(Stream& stream, TinyVGHeader const& header, ReadonlySpan<Color> color_table)
  159. : m_stream(stream)
  160. , m_scale(powf(0.5, header.scale))
  161. , m_coordinate_range(header.coordinate_range)
  162. , m_color_table(color_table)
  163. {
  164. }
  165. ErrorOr<float> read_unit()
  166. {
  167. auto read_value = [&]() -> ErrorOr<i32> {
  168. switch (m_coordinate_range) {
  169. case CoordinateRange::Default:
  170. return TRY(m_stream.read_value<LittleEndian<i16>>());
  171. case CoordinateRange::Reduced:
  172. return TRY(m_stream.read_value<i8>());
  173. case CoordinateRange::Enhanced:
  174. return TRY(m_stream.read_value<LittleEndian<i32>>());
  175. default:
  176. // Note: Already checked while reading the header.
  177. VERIFY_NOT_REACHED();
  178. }
  179. };
  180. return TRY(read_value()) * m_scale;
  181. }
  182. ErrorOr<u32> read_var_uint()
  183. {
  184. return TRY(m_stream.read_value<VarUInt>());
  185. }
  186. ErrorOr<FloatPoint> read_point()
  187. {
  188. return FloatPoint { TRY(read_unit()), TRY(read_unit()) };
  189. }
  190. ErrorOr<TinyVGDecodedImageData::Style> read_style(StyleType type)
  191. {
  192. auto read_color = [&]() -> ErrorOr<Color> {
  193. auto color_index = TRY(m_stream.read_value<VarUInt>());
  194. if (color_index >= m_color_table.size())
  195. return Error::from_string_literal("Invalid color table index");
  196. return m_color_table[color_index];
  197. };
  198. auto read_gradient = [&]() -> ErrorOr<NonnullRefPtr<SVGGradientPaintStyle>> {
  199. auto point_0 = TRY(read_point());
  200. auto point_1 = TRY(read_point());
  201. auto color_0 = TRY(read_color());
  202. auto color_1 = TRY(read_color());
  203. // Map TinyVG gradients to SVG gradients (since we already have those).
  204. // This is not entirely consistent with the spec, which uses gamma sRGB for gradients
  205. // (but this matches the TVG -> SVG renderings).
  206. auto svg_gradient = TRY([&]() -> ErrorOr<NonnullRefPtr<SVGGradientPaintStyle>> {
  207. if (type == StyleType::LinearGradient)
  208. return TRY(SVGLinearGradientPaintStyle::create(point_0, point_1));
  209. auto radius = point_1.distance_from(point_0);
  210. return TRY(SVGRadialGradientPaintStyle::create(point_0, 0, point_0, radius));
  211. }());
  212. TRY(svg_gradient->add_color_stop(0, color_0));
  213. TRY(svg_gradient->add_color_stop(1, color_1));
  214. return svg_gradient;
  215. };
  216. switch (type) {
  217. case StyleType::FlatColored:
  218. return TRY(read_color());
  219. case StyleType::LinearGradient:
  220. case StyleType::RadialGradinet:
  221. return TRY(read_gradient());
  222. }
  223. return Error::from_string_literal("Invalid TinyVG: Bad style data");
  224. }
  225. ErrorOr<FloatRect> read_rectangle()
  226. {
  227. return FloatRect { TRY(read_unit()), TRY(read_unit()), TRY(read_unit()), TRY(read_unit()) };
  228. }
  229. ErrorOr<FloatLine> read_line()
  230. {
  231. return FloatLine { TRY(read_point()), TRY(read_point()) };
  232. }
  233. ErrorOr<DeprecatedPath> read_path(u32 segment_count)
  234. {
  235. DeprecatedPath path;
  236. auto segment_lengths = TRY(FixedArray<u32>::create(segment_count));
  237. for (auto& command_count : segment_lengths) {
  238. command_count = TRY(read_var_uint()) + 1;
  239. }
  240. for (auto command_count : segment_lengths) {
  241. auto start_point = TRY(read_point());
  242. path.move_to(start_point);
  243. for (u32 i = 0; i < command_count; i++) {
  244. u8 command_tag = TRY(m_stream.read_value<u8>());
  245. auto path_command = static_cast<PathCommand>(command_tag & 0x7);
  246. bool has_line_width = (command_tag >> 4) & 0b1;
  247. if (has_line_width) {
  248. // FIXME: TinyVG allows changing the line width within a path.
  249. // This is not supported in LibGfx, so we currently ignore this.
  250. (void)TRY(read_unit());
  251. }
  252. switch (path_command) {
  253. case PathCommand::Line:
  254. path.line_to(TRY(read_point()));
  255. break;
  256. case PathCommand::HorizontalLine:
  257. path.line_to({ TRY(read_unit()), path.last_point().y() });
  258. break;
  259. case PathCommand::VerticalLine:
  260. path.line_to({ path.last_point().x(), TRY(read_unit()) });
  261. break;
  262. case PathCommand::CubicBezier: {
  263. auto control_0 = TRY(read_point());
  264. auto control_1 = TRY(read_point());
  265. auto point_1 = TRY(read_point());
  266. path.cubic_bezier_curve_to(control_0, control_1, point_1);
  267. break;
  268. }
  269. case PathCommand::ArcCircle: {
  270. u8 flags = TRY(m_stream.read_value<u8>());
  271. bool large_arc = (flags >> 0) & 0b1;
  272. bool sweep = (flags >> 1) & 0b1;
  273. auto radius = TRY(read_unit());
  274. auto target = TRY(read_point());
  275. path.arc_to(target, radius, large_arc, !sweep);
  276. break;
  277. }
  278. case PathCommand::ArcEllipse: {
  279. u8 flags = TRY(m_stream.read_value<u8>());
  280. bool large_arc = (flags >> 0) & 0b1;
  281. bool sweep = (flags >> 1) & 0b1;
  282. auto radius_x = TRY(read_unit());
  283. auto radius_y = TRY(read_unit());
  284. auto rotation = TRY(read_unit());
  285. auto target = TRY(read_point());
  286. path.elliptical_arc_to(target, { radius_x, radius_y }, rotation, large_arc, !sweep);
  287. break;
  288. }
  289. case PathCommand::ClosePath: {
  290. path.close();
  291. break;
  292. }
  293. case PathCommand::QuadraticBezier: {
  294. auto control = TRY(read_point());
  295. auto point_1 = TRY(read_point());
  296. path.quadratic_bezier_curve_to(control, point_1);
  297. break;
  298. }
  299. default:
  300. return Error::from_string_literal("Invalid TinyVG: Bad path command");
  301. }
  302. }
  303. }
  304. return path;
  305. }
  306. ErrorOr<FillCommandHeader> read_fill_command_header(StyleType style_type)
  307. {
  308. return FillCommandHeader { TRY(read_var_uint()) + 1, TRY(read_style(style_type)) };
  309. }
  310. ErrorOr<DrawCommandHeader> read_draw_command_header(StyleType style_type)
  311. {
  312. return DrawCommandHeader { TRY(read_var_uint()) + 1, TRY(read_style(style_type)), TRY(read_unit()) };
  313. }
  314. ErrorOr<OutlineFillCommandHeader> read_outline_fill_command_header(StyleType style_type)
  315. {
  316. u8 header = TRY(m_stream.read_value<u8>());
  317. u8 count = (header & 0x3f) + 1;
  318. auto stroke_type = static_cast<StyleType>((header >> 6) & 0x3);
  319. return OutlineFillCommandHeader { count, TRY(read_style(style_type)), TRY(read_style(stroke_type)), TRY(read_unit()) };
  320. }
  321. private:
  322. Stream& m_stream;
  323. float m_scale {};
  324. CoordinateRange m_coordinate_range;
  325. ReadonlySpan<Color> m_color_table;
  326. };
  327. ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> TinyVGDecodedImageData::decode(Stream& stream)
  328. {
  329. return decode(stream, TRY(decode_tinyvg_header(stream)));
  330. }
  331. ErrorOr<NonnullRefPtr<TinyVGDecodedImageData>> TinyVGDecodedImageData::decode(Stream& stream, TinyVGHeader const& header)
  332. {
  333. if (header.version != 1)
  334. return Error::from_string_literal("Invalid TinyVG: Unsupported version");
  335. auto color_table = TRY(decode_color_table(stream, header.color_encoding, header.color_count));
  336. TinyVGReader reader { stream, header, color_table.span() };
  337. auto rectangle_to_path = [](FloatRect const& rect) -> DeprecatedPath {
  338. DeprecatedPath path;
  339. path.move_to({ rect.x(), rect.y() });
  340. path.line_to({ rect.x() + rect.width(), rect.y() });
  341. path.line_to({ rect.x() + rect.width(), rect.y() + rect.height() });
  342. path.line_to({ rect.x(), rect.y() + rect.height() });
  343. path.close();
  344. return path;
  345. };
  346. Vector<DrawCommand> draw_commands;
  347. bool at_end = false;
  348. while (!at_end) {
  349. u8 command_info = TRY(stream.read_value<u8>());
  350. auto command = static_cast<Command>(command_info & 0x3f);
  351. auto style_type = static_cast<StyleType>((command_info >> 6) & 0x3);
  352. switch (command) {
  353. case Command::EndOfDocument:
  354. at_end = true;
  355. break;
  356. case Command::FillPolygon: {
  357. auto header = TRY(reader.read_fill_command_header(style_type));
  358. DeprecatedPath polygon;
  359. polygon.move_to(TRY(reader.read_point()));
  360. for (u32 i = 0; i < header.count - 1; i++)
  361. polygon.line_to(TRY(reader.read_point()));
  362. TRY(draw_commands.try_append(DrawCommand { move(polygon), move(header.style) }));
  363. break;
  364. }
  365. case Command::FillRectangles: {
  366. auto header = TRY(reader.read_fill_command_header(style_type));
  367. for (u32 i = 0; i < header.count; i++) {
  368. TRY(draw_commands.try_append(DrawCommand {
  369. rectangle_to_path(TRY(reader.read_rectangle())), header.style }));
  370. }
  371. break;
  372. }
  373. case Command::FillPath: {
  374. auto header = TRY(reader.read_fill_command_header(style_type));
  375. auto path = TRY(reader.read_path(header.count));
  376. TRY(draw_commands.try_append(DrawCommand { move(path), move(header.style) }));
  377. break;
  378. }
  379. case Command::DrawLines: {
  380. auto header = TRY(reader.read_draw_command_header(style_type));
  381. DeprecatedPath path;
  382. for (u32 i = 0; i < header.count; i++) {
  383. auto line = TRY(reader.read_line());
  384. path.move_to(line.a());
  385. path.line_to(line.b());
  386. }
  387. TRY(draw_commands.try_append(DrawCommand { move(path), {}, move(header.line_style), header.line_width }));
  388. break;
  389. }
  390. case Command::DrawLineStrip:
  391. case Command::DrawLineLoop: {
  392. auto header = TRY(reader.read_draw_command_header(style_type));
  393. DeprecatedPath path;
  394. path.move_to(TRY(reader.read_point()));
  395. for (u32 i = 0; i < header.count - 1; i++)
  396. path.line_to(TRY(reader.read_point()));
  397. if (command == Command::DrawLineLoop)
  398. path.close();
  399. TRY(draw_commands.try_append(DrawCommand { move(path), {}, move(header.line_style), header.line_width }));
  400. break;
  401. }
  402. case Command::DrawLinePath: {
  403. auto header = TRY(reader.read_draw_command_header(style_type));
  404. auto path = TRY(reader.read_path(header.count));
  405. TRY(draw_commands.try_append(DrawCommand { move(path), {}, move(header.line_style), header.line_width }));
  406. break;
  407. }
  408. case Command::OutlineFillPolygon: {
  409. auto header = TRY(reader.read_outline_fill_command_header(style_type));
  410. DeprecatedPath polygon;
  411. polygon.move_to(TRY(reader.read_point()));
  412. for (u32 i = 0; i < header.count - 1; i++)
  413. polygon.line_to(TRY(reader.read_point()));
  414. polygon.close();
  415. TRY(draw_commands.try_append(DrawCommand { move(polygon), move(header.fill_style), move(header.line_style), header.line_width }));
  416. break;
  417. }
  418. case Command::OutlineFillRectangles: {
  419. auto header = TRY(reader.read_outline_fill_command_header(style_type));
  420. for (u32 i = 0; i < header.count; i++) {
  421. TRY(draw_commands.try_append(DrawCommand {
  422. rectangle_to_path(TRY(reader.read_rectangle())), header.fill_style, header.line_style, header.line_width }));
  423. }
  424. break;
  425. }
  426. case Command::OutLineFillPath: {
  427. auto header = TRY(reader.read_outline_fill_command_header(style_type));
  428. auto path = TRY(reader.read_path(header.count));
  429. TRY(draw_commands.try_append(DrawCommand { move(path), move(header.fill_style), move(header.line_style), header.line_width }));
  430. break;
  431. }
  432. default:
  433. return Error::from_string_literal("Invalid TinyVG: Bad command");
  434. }
  435. }
  436. return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) TinyVGDecodedImageData({ header.width, header.height }, move(draw_commands))));
  437. }
  438. void TinyVGDecodedImageData::draw_transformed(DeprecatedPainter& painter, AffineTransform transform) const
  439. {
  440. // FIXME: Correctly handle non-uniform scales.
  441. auto scale = max(transform.x_scale(), transform.y_scale());
  442. AntiAliasingPainter aa_painter { painter };
  443. for (auto const& command : draw_commands()) {
  444. auto draw_path = command.path.copy_transformed(transform);
  445. if (command.fill.has_value()) {
  446. auto fill_path = draw_path;
  447. fill_path.close_all_subpaths();
  448. command.fill->visit(
  449. [&](Color color) { aa_painter.fill_path(fill_path, color, WindingRule::EvenOdd); },
  450. [&](NonnullRefPtr<SVGGradientPaintStyle> style) {
  451. const_cast<SVGGradientPaintStyle&>(*style).set_gradient_transform(transform);
  452. aa_painter.fill_path(fill_path, style, 1.0f, WindingRule::EvenOdd);
  453. });
  454. }
  455. if (command.stroke.has_value()) {
  456. command.stroke->visit(
  457. [&](Color color) { aa_painter.stroke_path(draw_path, color, command.stroke_width * scale); },
  458. [&](NonnullRefPtr<SVGGradientPaintStyle> style) {
  459. const_cast<SVGGradientPaintStyle&>(*style).set_gradient_transform(transform);
  460. aa_painter.stroke_path(draw_path, style, command.stroke_width * scale);
  461. });
  462. }
  463. }
  464. }
  465. struct TinyVGLoadingContext {
  466. FixedMemoryStream stream;
  467. TinyVGHeader header {};
  468. RefPtr<TinyVGDecodedImageData> decoded_image {};
  469. RefPtr<Bitmap> bitmap {};
  470. enum class State {
  471. NotDecoded = 0,
  472. HeaderDecoded,
  473. ImageDecoded,
  474. Error,
  475. };
  476. State state { State::NotDecoded };
  477. };
  478. static ErrorOr<void> decode_header_and_update_context(TinyVGLoadingContext& context)
  479. {
  480. VERIFY(context.state == TinyVGLoadingContext::State::NotDecoded);
  481. context.header = TRY(decode_tinyvg_header(context.stream));
  482. context.state = TinyVGLoadingContext::State::HeaderDecoded;
  483. return {};
  484. }
  485. static ErrorOr<void> decode_image_data_and_update_context(TinyVGLoadingContext& context)
  486. {
  487. VERIFY(context.state == TinyVGLoadingContext::State::HeaderDecoded);
  488. auto image_data_or_error = TinyVGDecodedImageData::decode(context.stream, context.header);
  489. if (image_data_or_error.is_error()) {
  490. context.state = TinyVGLoadingContext::State::Error;
  491. return image_data_or_error.release_error();
  492. }
  493. context.state = TinyVGLoadingContext::State::ImageDecoded;
  494. context.decoded_image = image_data_or_error.release_value();
  495. return {};
  496. }
  497. static ErrorOr<void> ensure_fully_decoded(TinyVGLoadingContext& context)
  498. {
  499. if (context.state == TinyVGLoadingContext::State::Error)
  500. return Error::from_string_literal("TinyVGImageDecoderPlugin: Decoding failed!");
  501. if (context.state == TinyVGLoadingContext::State::HeaderDecoded)
  502. TRY(decode_image_data_and_update_context(context));
  503. VERIFY(context.state == TinyVGLoadingContext::State::ImageDecoded);
  504. return {};
  505. }
  506. TinyVGImageDecoderPlugin::TinyVGImageDecoderPlugin(ReadonlyBytes bytes)
  507. : m_context { make<TinyVGLoadingContext>(FixedMemoryStream { bytes }) }
  508. {
  509. }
  510. TinyVGImageDecoderPlugin::~TinyVGImageDecoderPlugin() = default;
  511. ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> TinyVGImageDecoderPlugin::create(ReadonlyBytes bytes)
  512. {
  513. auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TinyVGImageDecoderPlugin(bytes)));
  514. TRY(decode_header_and_update_context(*plugin->m_context));
  515. return plugin;
  516. }
  517. bool TinyVGImageDecoderPlugin::sniff(ReadonlyBytes bytes)
  518. {
  519. FixedMemoryStream stream { { bytes.data(), bytes.size() } };
  520. return !decode_tinyvg_header(stream).is_error();
  521. }
  522. IntSize TinyVGImageDecoderPlugin::size()
  523. {
  524. return { m_context->header.width, m_context->header.height };
  525. }
  526. ErrorOr<ImageFrameDescriptor> TinyVGImageDecoderPlugin::frame(size_t, Optional<IntSize> ideal_size)
  527. {
  528. TRY(ensure_fully_decoded(*m_context));
  529. auto target_size = ideal_size.value_or(m_context->decoded_image->size());
  530. if (!m_context->bitmap || m_context->bitmap->size() != target_size)
  531. m_context->bitmap = TRY(m_context->decoded_image->bitmap(target_size));
  532. return ImageFrameDescriptor { m_context->bitmap };
  533. }
  534. ErrorOr<VectorImageFrameDescriptor> TinyVGImageDecoderPlugin::vector_frame(size_t)
  535. {
  536. TRY(ensure_fully_decoded(*m_context));
  537. return VectorImageFrameDescriptor { m_context->decoded_image, 0 };
  538. }
  539. }