| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | /*
 | 
					
						
							|  |  |  |  |  * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com> | 
					
						
							| 
									
										
										
										
											2025-01-29 10:24:57 +01:00
										 |  |  |  |  * Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org> | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |  * | 
					
						
							|  |  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #include <core/SkBitmap.h>
 | 
					
						
							|  |  |  |  | #include <core/SkBlurTypes.h>
 | 
					
						
							|  |  |  |  | #include <core/SkCanvas.h>
 | 
					
						
							| 
									
										
										
										
											2024-07-25 21:59:30 +03:00
										 |  |  |  | #include <core/SkFont.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | #include <core/SkMaskFilter.h>
 | 
					
						
							|  |  |  |  | #include <core/SkPath.h>
 | 
					
						
							| 
									
										
										
										
											2024-07-12 13:52:21 +01:00
										 |  |  |  | #include <core/SkPathEffect.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | #include <core/SkRRect.h>
 | 
					
						
							|  |  |  |  | #include <core/SkSurface.h>
 | 
					
						
							| 
									
										
										
										
											2024-07-12 13:52:21 +01:00
										 |  |  |  | #include <effects/SkDashPathEffect.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | #include <effects/SkGradientShader.h>
 | 
					
						
							|  |  |  |  | #include <effects/SkImageFilters.h>
 | 
					
						
							| 
									
										
										
										
											2024-09-26 14:43:37 +02:00
										 |  |  |  | #include <effects/SkRuntimeEffect.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-26 17:56:55 +02:00
										 |  |  |  | #include <gpu/GrDirectContext.h>
 | 
					
						
							|  |  |  |  | #include <gpu/ganesh/SkSurfaceGanesh.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | #include <pathops/SkPathOps.h>
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 21:59:30 +03:00
										 |  |  |  | #include <LibGfx/Font/ScaledFont.h>
 | 
					
						
							| 
									
										
										
										
											2025-01-22 09:50:49 +01:00
										 |  |  |  | #include <LibGfx/PainterSkia.h>
 | 
					
						
							|  |  |  |  | #include <LibGfx/PathSkia.h>
 | 
					
						
							| 
									
										
										
										
											2024-12-18 11:34:25 +01:00
										 |  |  |  | #include <LibGfx/SkiaUtils.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | #include <LibWeb/CSS/ComputedValues.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-23 18:42:39 +02:00
										 |  |  |  | #include <LibWeb/Painting/DisplayListPlayerSkia.h>
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | #include <LibWeb/Painting/ShadowPainting.h>
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | namespace Web::Painting { | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-29 10:24:57 +01:00
										 |  |  |  | DisplayListPlayerSkia::DisplayListPlayerSkia(RefPtr<Gfx::SkiaBackendContext> context) | 
					
						
							| 
									
										
										
										
											2024-09-25 15:42:15 +02:00
										 |  |  |  |     : m_context(context) | 
					
						
							| 
									
										
										
										
											2024-06-26 17:56:55 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-29 10:24:57 +01:00
										 |  |  |  | DisplayListPlayerSkia::DisplayListPlayerSkia() | 
					
						
							| 
									
										
										
										
											2024-06-26 17:56:55 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-19 03:37:03 +03:00
										 |  |  |  | static SkRRect to_skia_rrect(auto const& rect, CornerRadii const& corner_radii) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     SkRRect rrect; | 
					
						
							|  |  |  |  |     SkVector radii[4]; | 
					
						
							|  |  |  |  |     radii[0].set(corner_radii.top_left.horizontal_radius, corner_radii.top_left.vertical_radius); | 
					
						
							|  |  |  |  |     radii[1].set(corner_radii.top_right.horizontal_radius, corner_radii.top_right.vertical_radius); | 
					
						
							|  |  |  |  |     radii[2].set(corner_radii.bottom_right.horizontal_radius, corner_radii.bottom_right.vertical_radius); | 
					
						
							|  |  |  |  |     radii[3].set(corner_radii.bottom_left.horizontal_radius, corner_radii.bottom_left.vertical_radius); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     rrect.setRectRadii(to_skia_rect(rect), radii); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     return rrect; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  | static SkMatrix to_skia_matrix(Gfx::AffineTransform const& affine_transform) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     SkScalar affine[6]; | 
					
						
							|  |  |  |  |     affine[0] = affine_transform.a(); | 
					
						
							|  |  |  |  |     affine[1] = affine_transform.b(); | 
					
						
							|  |  |  |  |     affine[2] = affine_transform.c(); | 
					
						
							|  |  |  |  |     affine[3] = affine_transform.d(); | 
					
						
							|  |  |  |  |     affine[4] = affine_transform.e(); | 
					
						
							|  |  |  |  |     affine[5] = affine_transform.f(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkMatrix matrix; | 
					
						
							|  |  |  |  |     matrix.setAffine(affine); | 
					
						
							|  |  |  |  |     return matrix; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-29 10:24:57 +01:00
										 |  |  |  | void DisplayListPlayerSkia::flush() | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-01-29 10:24:57 +01:00
										 |  |  |  |     if (m_context) | 
					
						
							|  |  |  |  |         m_context->flush_and_submit(&surface().sk_surface()); | 
					
						
							|  |  |  |  |     surface().flush(); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-25 21:59:30 +03:00
										 |  |  |  |     auto const& gfx_font = static_cast<Gfx::ScaledFont const&>(command.glyph_run->font()); | 
					
						
							|  |  |  |  |     auto sk_font = gfx_font.skia_font(command.scale); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto glyph_count = command.glyph_run->glyphs().size(); | 
					
						
							|  |  |  |  |     Vector<SkGlyphID> glyphs; | 
					
						
							|  |  |  |  |     glyphs.ensure_capacity(glyph_count); | 
					
						
							|  |  |  |  |     Vector<SkPoint> positions; | 
					
						
							|  |  |  |  |     positions.ensure_capacity(glyph_count); | 
					
						
							|  |  |  |  |     auto font_ascent = gfx_font.pixel_metrics().ascent; | 
					
						
							| 
									
										
										
										
											2024-09-06 01:19:53 +02:00
										 |  |  |  |     for (auto const& glyph : command.glyph_run->glyphs()) { | 
					
						
							|  |  |  |  |         auto transformed_glyph = glyph; | 
					
						
							|  |  |  |  |         transformed_glyph.position.set_y(glyph.position.y() + font_ascent); | 
					
						
							|  |  |  |  |         transformed_glyph.position = transformed_glyph.position.scaled(command.scale); | 
					
						
							|  |  |  |  |         auto const& point = transformed_glyph.position; | 
					
						
							|  |  |  |  |         glyphs.append(transformed_glyph.glyph_id); | 
					
						
							|  |  |  |  |         positions.append(to_skia_point(point)); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-07-25 21:59:30 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							| 
									
										
										
										
											2024-10-29 14:29:00 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     switch (command.orientation) { | 
					
						
							|  |  |  |  |     case Gfx::Orientation::Horizontal: | 
					
						
							|  |  |  |  |         canvas.drawGlyphs(glyphs.size(), glyphs.data(), positions.data(), to_skia_point(command.translation), sk_font, paint); | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  |     case Gfx::Orientation::Vertical: | 
					
						
							|  |  |  |  |         canvas.save(); | 
					
						
							|  |  |  |  |         canvas.translate(command.rect.width(), 0); | 
					
						
							|  |  |  |  |         canvas.rotate(90, command.rect.top_left().x(), command.rect.top_left().y()); | 
					
						
							|  |  |  |  |         canvas.drawGlyphs(glyphs.size(), glyphs.data(), positions.data(), to_skia_point(command.translation), sk_font, paint); | 
					
						
							|  |  |  |  |         canvas.restore(); | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::fill_rect(FillRect const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto const& rect = command.rect; | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							|  |  |  |  |     canvas.drawRect(to_skia_rect(rect), paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 15:44:58 +02:00
										 |  |  |  | void DisplayListPlayerSkia::draw_painting_surface(DrawPaintingSurface const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto src_rect = to_skia_rect(command.src_rect); | 
					
						
							|  |  |  |  |     auto dst_rect = to_skia_rect(command.dst_rect); | 
					
						
							|  |  |  |  |     auto& sk_surface = command.surface->sk_surface(); | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     auto image = sk_surface.makeImageSnapshot(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     canvas.drawImageRect(image, src_rect, dst_rect, to_skia_sampling_options(command.scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto dst_rect = to_skia_rect(command.dst_rect); | 
					
						
							| 
									
										
										
										
											2025-02-04 21:51:51 +01:00
										 |  |  |  |     auto clip_rect = to_skia_rect(command.clip_rect); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2025-02-04 21:51:51 +01:00
										 |  |  |  |     canvas.save(); | 
					
						
							|  |  |  |  |     canvas.clipRect(clip_rect); | 
					
						
							|  |  |  |  |     canvas.drawImageRect(command.bitmap->sk_image(), dst_rect, to_skia_sampling_options(command.scaling_mode), &paint); | 
					
						
							|  |  |  |  |     canvas.restore(); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const& command) | 
					
						
							| 
									
										
										
										
											2024-07-23 15:36:28 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     SkMatrix matrix; | 
					
						
							|  |  |  |  |     auto dst_rect = command.dst_rect.to_type<float>(); | 
					
						
							|  |  |  |  |     auto src_size = command.bitmap->size().to_type<float>(); | 
					
						
							|  |  |  |  |     matrix.setScale(dst_rect.width() / src_size.width(), dst_rect.height() / src_size.height()); | 
					
						
							|  |  |  |  |     matrix.postTranslate(dst_rect.x(), dst_rect.y()); | 
					
						
							|  |  |  |  |     auto sampling_options = to_skia_sampling_options(command.scaling_mode); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto tile_mode_x = command.repeat.x ? SkTileMode::kRepeat : SkTileMode::kDecal; | 
					
						
							|  |  |  |  |     auto tile_mode_y = command.repeat.y ? SkTileMode::kRepeat : SkTileMode::kDecal; | 
					
						
							| 
									
										
										
										
											2024-11-09 05:48:17 +01:00
										 |  |  |  |     auto shader = command.bitmap->sk_image()->makeShader(tile_mode_x, tile_mode_y, sampling_options, matrix); | 
					
						
							| 
									
										
										
										
											2024-07-23 15:36:28 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setShader(shader); | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     canvas.drawPaint(paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::add_clip_rect(AddClipRect const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     auto const& rect = command.rect; | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     canvas.clipRect(to_skia_rect(rect)); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::save(Save const&) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     canvas.save(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::restore(Restore const&) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     canvas.restore(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-11 15:01:16 +02:00
										 |  |  |  | void DisplayListPlayerSkia::translate(Translate const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     canvas.translate(command.delta.x(), command.delta.y()); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::push_stacking_context(PushStackingContext const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto affine_transform = Gfx::extract_2d_affine_transform(command.transform.matrix); | 
					
						
							|  |  |  |  |     auto new_transform = Gfx::AffineTransform {} | 
					
						
							|  |  |  |  |                              .translate(command.transform.origin) | 
					
						
							|  |  |  |  |                              .multiply(affine_transform) | 
					
						
							|  |  |  |  |                              .translate(-command.transform.origin); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto matrix = to_skia_matrix(new_transform); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-22 09:50:49 +01:00
										 |  |  |  |     if (command.opacity < 1 || command.compositing_and_blending_operator != Gfx::CompositingAndBlendingOperator::Normal || command.isolate) { | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |         auto source_paintable_rect = to_skia_rect(command.source_paintable_rect); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |         SkRect dest; | 
					
						
							|  |  |  |  |         matrix.mapRect(&dest, source_paintable_rect); | 
					
						
							| 
									
										
										
										
											2025-01-22 09:50:49 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |         SkPaint paint; | 
					
						
							|  |  |  |  |         paint.setAlphaf(command.opacity); | 
					
						
							|  |  |  |  |         paint.setBlender(Gfx::to_skia_blender(command.compositing_and_blending_operator)); | 
					
						
							|  |  |  |  |         canvas.saveLayer(&dest, &paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     } else { | 
					
						
							|  |  |  |  |         canvas.save(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-25 15:37:21 +02:00
										 |  |  |  |     if (command.clip_path.has_value()) | 
					
						
							| 
									
										
										
										
											2024-08-20 17:27:08 +02:00
										 |  |  |  |         canvas.clipPath(to_skia_path(command.clip_path.value()), true); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     canvas.concat(matrix); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::pop_stacking_context(PopStackingContext const&) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     surface().canvas().restore(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  | static ColorStopList replace_transition_hints_with_normal_color_stops(ColorStopList const& color_stop_list) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     ColorStopList stops_with_replaced_transition_hints; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto const& first_color_stop = color_stop_list.first(); | 
					
						
							|  |  |  |  |     // First color stop in the list should never have transition hint value
 | 
					
						
							|  |  |  |  |     VERIFY(!first_color_stop.transition_hint.has_value()); | 
					
						
							|  |  |  |  |     stops_with_replaced_transition_hints.empend(first_color_stop.color, first_color_stop.position); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // This loop replaces transition hints with five regular points, calculated using the
 | 
					
						
							|  |  |  |  |     // formula defined in the spec. After rendering using linear interpolation, this will
 | 
					
						
							|  |  |  |  |     // produce a result close enough to that obtained if the color of each point were calculated
 | 
					
						
							|  |  |  |  |     // using the non-linear formula from the spec.
 | 
					
						
							|  |  |  |  |     for (size_t i = 1; i < color_stop_list.size(); i++) { | 
					
						
							|  |  |  |  |         auto const& color_stop = color_stop_list[i]; | 
					
						
							|  |  |  |  |         if (!color_stop.transition_hint.has_value()) { | 
					
						
							|  |  |  |  |             stops_with_replaced_transition_hints.empend(color_stop.color, color_stop.position); | 
					
						
							|  |  |  |  |             continue; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         auto const& previous_color_stop = color_stop_list[i - 1]; | 
					
						
							|  |  |  |  |         auto const& next_color_stop = color_stop_list[i]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         auto distance_between_stops = next_color_stop.position - previous_color_stop.position; | 
					
						
							|  |  |  |  |         auto transition_hint = color_stop.transition_hint.value(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-15 14:40:18 +01:00
										 |  |  |  |         Array transition_hint_relative_sampling_positions { | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |             transition_hint * 0.33f, | 
					
						
							|  |  |  |  |             transition_hint * 0.66f, | 
					
						
							|  |  |  |  |             transition_hint, | 
					
						
							| 
									
										
										
										
											2024-11-15 14:40:18 +01:00
										 |  |  |  |             transition_hint + (1.f - transition_hint) * 0.33f, | 
					
						
							|  |  |  |  |             transition_hint + (1.f - transition_hint) * 0.66f, | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |         }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         for (auto const& transition_hint_relative_sampling_position : transition_hint_relative_sampling_positions) { | 
					
						
							|  |  |  |  |             auto position = previous_color_stop.position + transition_hint_relative_sampling_position * distance_between_stops; | 
					
						
							|  |  |  |  |             auto value = color_stop_step(previous_color_stop, next_color_stop, position); | 
					
						
							|  |  |  |  |             auto color = previous_color_stop.color.interpolate(next_color_stop.color, value); | 
					
						
							|  |  |  |  |             stops_with_replaced_transition_hints.empend(color, position); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         stops_with_replaced_transition_hints.empend(color_stop.color, color_stop.position); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return stops_with_replaced_transition_hints; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-15 16:16:06 +03:00
										 |  |  |  | static ColorStopList expand_repeat_length(ColorStopList const& color_stop_list, float repeat_length) | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-16 16:42:25 +03:00
										 |  |  |  |     // https://drafts.csswg.org/css-images/#repeating-gradients
 | 
					
						
							|  |  |  |  |     // When rendered, however, the color-stops are repeated infinitely in both directions, with their
 | 
					
						
							|  |  |  |  |     // positions shifted by multiples of the difference between the last specified color-stop’s position
 | 
					
						
							|  |  |  |  |     // and the first specified color-stop’s position. For example, repeating-linear-gradient(red 10px, blue 50px)
 | 
					
						
							|  |  |  |  |     // is equivalent to linear-gradient(..., red -30px, blue 10px, red 10px, blue 50px, red 50px, blue 90px, ...).
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto first_stop_position = color_stop_list.first().position; | 
					
						
							|  |  |  |  |     int const negative_repeat_count = AK::ceil(first_stop_position / repeat_length); | 
					
						
							|  |  |  |  |     int const positive_repeat_count = AK::ceil((1.0f - first_stop_position) / repeat_length); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     ColorStopList color_stop_list_with_expanded_repeat = color_stop_list; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto get_color_between_stops = [](float position, auto const& current_stop, auto const& previous_stop) { | 
					
						
							|  |  |  |  |         auto distance_between_stops = current_stop.position - previous_stop.position; | 
					
						
							|  |  |  |  |         auto percentage = (position - previous_stop.position) / distance_between_stops; | 
					
						
							|  |  |  |  |         return previous_stop.color.interpolate(current_stop.color, percentage); | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (auto repeat_count = 1; repeat_count <= negative_repeat_count; repeat_count++) { | 
					
						
							|  |  |  |  |         for (auto stop : color_stop_list.in_reverse()) { | 
					
						
							|  |  |  |  |             stop.position += repeat_length * static_cast<float>(-repeat_count); | 
					
						
							|  |  |  |  |             if (stop.position < 0) { | 
					
						
							|  |  |  |  |                 stop.color = get_color_between_stops(0.0f, stop, color_stop_list_with_expanded_repeat.first()); | 
					
						
							|  |  |  |  |                 color_stop_list_with_expanded_repeat.prepend(stop); | 
					
						
							|  |  |  |  |                 break; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |             color_stop_list_with_expanded_repeat.prepend(stop); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (auto repeat_count = 0; repeat_count < positive_repeat_count; repeat_count++) { | 
					
						
							| 
									
										
										
										
											2024-07-15 16:16:06 +03:00
										 |  |  |  |         for (auto stop : color_stop_list) { | 
					
						
							| 
									
										
										
										
											2024-07-16 16:42:25 +03:00
										 |  |  |  |             stop.position += repeat_length * static_cast<float>(repeat_count); | 
					
						
							|  |  |  |  |             if (stop.position > 1) { | 
					
						
							|  |  |  |  |                 stop.color = get_color_between_stops(1.0f, stop, color_stop_list_with_expanded_repeat.last()); | 
					
						
							|  |  |  |  |                 color_stop_list_with_expanded_repeat.append(stop); | 
					
						
							|  |  |  |  |                 break; | 
					
						
							|  |  |  |  |             } | 
					
						
							| 
									
										
										
										
											2024-07-15 16:16:06 +03:00
										 |  |  |  |             color_stop_list_with_expanded_repeat.append(stop); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-07-16 16:42:25 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     return color_stop_list_with_expanded_repeat; | 
					
						
							| 
									
										
										
										
											2024-07-15 16:16:06 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::paint_linear_gradient(PaintLinearGradient const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto const& linear_gradient_data = command.linear_gradient_data; | 
					
						
							| 
									
										
										
										
											2024-07-16 16:42:48 +03:00
										 |  |  |  |     auto color_stop_list = linear_gradient_data.color_stops.list; | 
					
						
							|  |  |  |  |     auto const& repeat_length = linear_gradient_data.color_stops.repeat_length; | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |     VERIFY(!color_stop_list.is_empty()); | 
					
						
							| 
									
										
										
										
											2024-07-16 16:42:48 +03:00
										 |  |  |  |     if (repeat_length.has_value()) | 
					
						
							|  |  |  |  |         color_stop_list = expand_repeat_length(color_stop_list, *repeat_length); | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto stops_with_replaced_transition_hints = replace_transition_hints_with_normal_color_stops(color_stop_list); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |     Vector<SkColor4f> colors; | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     Vector<SkScalar> positions; | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |     for (size_t stop_index = 0; stop_index < stops_with_replaced_transition_hints.size(); stop_index++) { | 
					
						
							|  |  |  |  |         auto const& stop = stops_with_replaced_transition_hints[stop_index]; | 
					
						
							|  |  |  |  |         if (stop_index > 0 && stop == stops_with_replaced_transition_hints[stop_index - 1]) | 
					
						
							|  |  |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |         colors.append(to_skia_color4f(stop.color)); | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |         positions.append(stop.position); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto const& rect = command.gradient_rect; | 
					
						
							|  |  |  |  |     auto length = calculate_gradient_length<int>(rect.size(), linear_gradient_data.gradient_angle); | 
					
						
							|  |  |  |  |     auto bottom = rect.center().translated(0, -length / 2); | 
					
						
							|  |  |  |  |     auto top = rect.center().translated(0, length / 2); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-15 14:40:18 +01:00
										 |  |  |  |     Array points { | 
					
						
							|  |  |  |  |         to_skia_point(top), | 
					
						
							|  |  |  |  |         to_skia_point(bottom), | 
					
						
							|  |  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto center = to_skia_rect(rect).center(); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     SkMatrix matrix; | 
					
						
							|  |  |  |  |     matrix.setRotate(linear_gradient_data.gradient_angle, center.x(), center.y()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |     auto color_space = SkColorSpace::MakeSRGB(); | 
					
						
							|  |  |  |  |     SkGradientShader::Interpolation interpolation = {}; | 
					
						
							|  |  |  |  |     interpolation.fColorSpace = SkGradientShader::Interpolation::ColorSpace::kSRGB; | 
					
						
							|  |  |  |  |     interpolation.fInPremul = SkGradientShader::Interpolation::InPremul::kYes; | 
					
						
							|  |  |  |  |     auto shader = SkGradientShader::MakeLinear(points.data(), colors.data(), color_space, positions.data(), positions.size(), SkTileMode::kClamp, interpolation, &matrix); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setShader(shader); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     surface().canvas().drawRect(to_skia_rect(rect), paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void add_spread_distance_to_border_radius(int& border_radius, int spread_distance) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     if (border_radius == 0 || spread_distance == 0) | 
					
						
							|  |  |  |  |         return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // https://drafts.csswg.org/css-backgrounds/#shadow-shape
 | 
					
						
							|  |  |  |  |     // To preserve the box’s shape when spread is applied, the corner radii of the shadow are also increased (decreased,
 | 
					
						
							|  |  |  |  |     // for inner shadows) from the border-box (padding-box) radii by adding (subtracting) the spread distance (and flooring
 | 
					
						
							|  |  |  |  |     // at zero). However, in order to create a sharper corner when the border radius is small (and thus ensure continuity
 | 
					
						
							|  |  |  |  |     // between round and sharp corners), when the border radius is less than the spread distance (or in the case of an inner
 | 
					
						
							|  |  |  |  |     // shadow, less than the absolute value of a negative spread distance), the spread distance is first multiplied by the
 | 
					
						
							|  |  |  |  |     // proportion 1 + (r-1)^3, where r is the ratio of the border radius to the spread distance, in calculating the corner
 | 
					
						
							|  |  |  |  |     // radii of the spread shadow shape.
 | 
					
						
							|  |  |  |  |     if (border_radius > AK::abs(spread_distance)) { | 
					
						
							|  |  |  |  |         border_radius += spread_distance; | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |         auto r = (float)border_radius / AK::abs(spread_distance); | 
					
						
							|  |  |  |  |         border_radius += spread_distance * (1 + AK::pow(r - 1, 3.0f)); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::paint_outer_box_shadow(PaintOuterBoxShadow const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto const& outer_box_shadow_params = command.box_shadow_params; | 
					
						
							|  |  |  |  |     auto const& color = outer_box_shadow_params.color; | 
					
						
							|  |  |  |  |     auto const& spread_distance = outer_box_shadow_params.spread_distance; | 
					
						
							|  |  |  |  |     auto const& blur_radius = outer_box_shadow_params.blur_radius; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto content_rrect = to_skia_rrect(outer_box_shadow_params.device_content_rect, outer_box_shadow_params.corner_radii); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto shadow_rect = outer_box_shadow_params.device_content_rect; | 
					
						
							|  |  |  |  |     shadow_rect.inflate(spread_distance, spread_distance, spread_distance, spread_distance); | 
					
						
							|  |  |  |  |     auto offset_x = outer_box_shadow_params.offset_x; | 
					
						
							|  |  |  |  |     auto offset_y = outer_box_shadow_params.offset_y; | 
					
						
							|  |  |  |  |     shadow_rect.translate_by(offset_x, offset_y); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto add_spread_distance_to_corner_radius = [&](auto& corner_radius) { | 
					
						
							|  |  |  |  |         add_spread_distance_to_border_radius(corner_radius.horizontal_radius, spread_distance); | 
					
						
							|  |  |  |  |         add_spread_distance_to_border_radius(corner_radius.vertical_radius, spread_distance); | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto corner_radii = outer_box_shadow_params.corner_radii; | 
					
						
							|  |  |  |  |     add_spread_distance_to_corner_radius(corner_radii.top_left); | 
					
						
							|  |  |  |  |     add_spread_distance_to_corner_radius(corner_radii.top_right); | 
					
						
							|  |  |  |  |     add_spread_distance_to_corner_radius(corner_radii.bottom_right); | 
					
						
							|  |  |  |  |     add_spread_distance_to_corner_radius(corner_radii.bottom_left); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     canvas.save(); | 
					
						
							|  |  |  |  |     canvas.clipRRect(content_rrect, SkClipOp::kDifference, true); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2024-07-19 14:57:09 +02:00
										 |  |  |  |     paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(color)); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, blur_radius / 2)); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto shadow_rounded_rect = to_skia_rrect(shadow_rect, corner_radii); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     canvas.drawRRect(shadow_rounded_rect, paint); | 
					
						
							|  |  |  |  |     canvas.restore(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::paint_inner_box_shadow(PaintInnerBoxShadow const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto const& outer_box_shadow_params = command.box_shadow_params; | 
					
						
							|  |  |  |  |     auto color = outer_box_shadow_params.color; | 
					
						
							|  |  |  |  |     auto device_content_rect = outer_box_shadow_params.device_content_rect; | 
					
						
							|  |  |  |  |     auto offset_x = outer_box_shadow_params.offset_x; | 
					
						
							|  |  |  |  |     auto offset_y = outer_box_shadow_params.offset_y; | 
					
						
							|  |  |  |  |     auto blur_radius = outer_box_shadow_params.blur_radius; | 
					
						
							|  |  |  |  |     auto spread_distance = outer_box_shadow_params.spread_distance; | 
					
						
							|  |  |  |  |     auto const& corner_radii = outer_box_shadow_params.corner_radii; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto outer_shadow_rect = device_content_rect.translated({ offset_x, offset_y }); | 
					
						
							|  |  |  |  |     auto inner_shadow_rect = outer_shadow_rect.inflated(-spread_distance, -spread_distance, -spread_distance, -spread_distance); | 
					
						
							|  |  |  |  |     outer_shadow_rect.inflate( | 
					
						
							|  |  |  |  |         blur_radius + offset_y, | 
					
						
							|  |  |  |  |         blur_radius + abs(offset_x), | 
					
						
							|  |  |  |  |         blur_radius + abs(offset_y), | 
					
						
							|  |  |  |  |         blur_radius + offset_x); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto inner_rect_corner_radii = corner_radii; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto add_spread_distance_to_corner_radius = [&](auto& corner_radius) { | 
					
						
							|  |  |  |  |         add_spread_distance_to_border_radius(corner_radius.horizontal_radius, -spread_distance); | 
					
						
							|  |  |  |  |         add_spread_distance_to_border_radius(corner_radius.vertical_radius, -spread_distance); | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     add_spread_distance_to_corner_radius(inner_rect_corner_radii.top_left); | 
					
						
							|  |  |  |  |     add_spread_distance_to_corner_radius(inner_rect_corner_radii.top_right); | 
					
						
							|  |  |  |  |     add_spread_distance_to_corner_radius(inner_rect_corner_radii.bottom_right); | 
					
						
							|  |  |  |  |     add_spread_distance_to_corner_radius(inner_rect_corner_radii.bottom_left); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto outer_rect = to_skia_rrect(outer_shadow_rect, corner_radii); | 
					
						
							|  |  |  |  |     auto inner_rect = to_skia_rrect(inner_shadow_rect, inner_rect_corner_radii); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPath outer_path; | 
					
						
							|  |  |  |  |     outer_path.addRRect(outer_rect); | 
					
						
							|  |  |  |  |     SkPath inner_path; | 
					
						
							|  |  |  |  |     inner_path.addRRect(inner_rect); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPath result_path; | 
					
						
							|  |  |  |  |     if (!Op(outer_path, inner_path, SkPathOp::kDifference_SkPathOp, &result_path)) { | 
					
						
							|  |  |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint path_paint; | 
					
						
							| 
									
										
										
										
											2024-07-19 14:57:09 +02:00
										 |  |  |  |     path_paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     path_paint.setColor(to_skia_color(color)); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     path_paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, blur_radius / 2)); | 
					
						
							|  |  |  |  |     canvas.save(); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     canvas.clipRRect(to_skia_rrect(device_content_rect, corner_radii), true); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     canvas.drawPath(result_path, path_paint); | 
					
						
							|  |  |  |  |     canvas.restore(); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::paint_text_shadow(PaintTextShadow const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-28 19:09:02 +02:00
										 |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							| 
									
										
										
										
											2024-07-17 18:19:13 +03:00
										 |  |  |  |     auto blur_image_filter = SkImageFilters::Blur(command.blur_radius / 2, command.blur_radius / 2, nullptr); | 
					
						
							| 
									
										
										
										
											2024-06-28 19:09:02 +02:00
										 |  |  |  |     SkPaint blur_paint; | 
					
						
							|  |  |  |  |     blur_paint.setImageFilter(blur_image_filter); | 
					
						
							|  |  |  |  |     canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, &blur_paint, nullptr, 0)); | 
					
						
							|  |  |  |  |     draw_glyph_run({ | 
					
						
							|  |  |  |  |         .glyph_run = command.glyph_run, | 
					
						
							| 
									
										
										
										
											2024-11-11 14:05:36 +04:00
										 |  |  |  |         .scale = command.glyph_run_scale, | 
					
						
							| 
									
										
										
										
											2024-06-28 19:09:02 +02:00
										 |  |  |  |         .rect = command.text_rect, | 
					
						
							| 
									
										
										
										
											2024-12-09 10:24:26 +01:00
										 |  |  |  |         .translation = command.draw_location + command.text_rect.location().to_type<float>(), | 
					
						
							| 
									
										
										
										
											2024-11-11 14:05:36 +04:00
										 |  |  |  |         .color = command.color, | 
					
						
							| 
									
										
										
										
											2024-06-28 19:09:02 +02:00
										 |  |  |  |     }); | 
					
						
							|  |  |  |  |     canvas.restore(); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::fill_rect_with_rounded_corners(FillRectWithRoundedCorners const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto const& rect = command.rect; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							| 
									
										
										
										
											2024-07-19 14:57:09 +02:00
										 |  |  |  |     paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-01 15:54:04 +02:00
										 |  |  |  |     auto rounded_rect = to_skia_rrect(rect, command.corner_radii); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     canvas.drawRRect(rounded_rect, paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::fill_path_using_color(FillPathUsingColor const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							|  |  |  |  |     auto path = to_skia_path(command.path); | 
					
						
							| 
									
										
										
										
											2024-06-19 17:19:17 +03:00
										 |  |  |  |     path.setFillType(to_skia_path_fill_type(command.winding_rule)); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     path.offset(command.aa_translation.x(), command.aa_translation.y()); | 
					
						
							|  |  |  |  |     canvas.drawPath(path, paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-14 18:29:33 +02:00
										 |  |  |  | static SkTileMode to_skia_tile_mode(SVGLinearGradientPaintStyle::SpreadMethod spread_method) | 
					
						
							| 
									
										
										
										
											2024-07-12 13:58:37 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     switch (spread_method) { | 
					
						
							|  |  |  |  |     case SVGLinearGradientPaintStyle::SpreadMethod::Pad: | 
					
						
							|  |  |  |  |         return SkTileMode::kClamp; | 
					
						
							|  |  |  |  |     case SVGLinearGradientPaintStyle::SpreadMethod::Reflect: | 
					
						
							|  |  |  |  |         return SkTileMode::kMirror; | 
					
						
							|  |  |  |  |     case SVGLinearGradientPaintStyle::SpreadMethod::Repeat: | 
					
						
							|  |  |  |  |         return SkTileMode::kRepeat; | 
					
						
							|  |  |  |  |     default: | 
					
						
							|  |  |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-14 18:29:33 +02:00
										 |  |  |  | static SkPaint paint_style_to_skia_paint(Painting::SVGGradientPaintStyle const& paint_style, Gfx::FloatRect bounding_rect) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2024-07-16 17:43:43 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto const& color_stops = paint_style.color_stops(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Vector<SkColor> colors; | 
					
						
							|  |  |  |  |     colors.ensure_capacity(color_stops.size()); | 
					
						
							|  |  |  |  |     Vector<SkScalar> positions; | 
					
						
							|  |  |  |  |     positions.ensure_capacity(color_stops.size()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (auto const& color_stop : color_stops) { | 
					
						
							|  |  |  |  |         colors.append(to_skia_color(color_stop.color)); | 
					
						
							|  |  |  |  |         positions.append(color_stop.position); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:31:11 +01:00
										 |  |  |  |     SkMatrix matrix; | 
					
						
							|  |  |  |  |     matrix.setTranslate(bounding_rect.x(), bounding_rect.y()); | 
					
						
							|  |  |  |  |     if (auto gradient_transform = paint_style.gradient_transform(); gradient_transform.has_value()) | 
					
						
							|  |  |  |  |         matrix = matrix * to_skia_matrix(gradient_transform.value()); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:31:11 +01:00
										 |  |  |  |     auto tile_mode = to_skia_tile_mode(paint_style.spread_method()); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:31:11 +01:00
										 |  |  |  |     sk_sp<SkShader> shader; | 
					
						
							|  |  |  |  |     if (is<SVGLinearGradientPaintStyle>(paint_style)) { | 
					
						
							|  |  |  |  |         auto const& linear_gradient_paint_style = static_cast<SVGLinearGradientPaintStyle const&>(paint_style); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-15 14:40:18 +01:00
										 |  |  |  |         Array points { | 
					
						
							| 
									
										
										
										
											2024-10-28 20:31:11 +01:00
										 |  |  |  |             to_skia_point(linear_gradient_paint_style.start_point()), | 
					
						
							|  |  |  |  |             to_skia_point(linear_gradient_paint_style.end_point()), | 
					
						
							|  |  |  |  |         }; | 
					
						
							|  |  |  |  |         shader = SkGradientShader::MakeLinear(points.data(), colors.data(), positions.data(), color_stops.size(), tile_mode, 0, &matrix); | 
					
						
							| 
									
										
										
										
											2024-07-16 17:43:43 +03:00
										 |  |  |  |     } else if (is<SVGRadialGradientPaintStyle>(paint_style)) { | 
					
						
							|  |  |  |  |         auto const& radial_gradient_paint_style = static_cast<SVGRadialGradientPaintStyle const&>(paint_style); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:31:11 +01:00
										 |  |  |  |         auto start_center = to_skia_point(radial_gradient_paint_style.start_center()); | 
					
						
							|  |  |  |  |         auto end_center = to_skia_point(radial_gradient_paint_style.end_center()); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:31:11 +01:00
										 |  |  |  |         auto start_radius = radial_gradient_paint_style.start_radius(); | 
					
						
							|  |  |  |  |         auto end_radius = radial_gradient_paint_style.end_radius(); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-28 20:31:11 +01:00
										 |  |  |  |         shader = SkGradientShader::MakeTwoPointConical(start_center, start_radius, end_center, end_radius, colors.data(), positions.data(), color_stops.size(), tile_mode, 0, &matrix); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-10-28 20:31:11 +01:00
										 |  |  |  |     paint.setShader(shader); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     return paint; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::fill_path_using_paint_style(FillPathUsingPaintStyle const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto path = to_skia_path(command.path); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     path.offset(command.aa_translation.x(), command.aa_translation.y()); | 
					
						
							| 
									
										
										
										
											2024-06-19 17:19:17 +03:00
										 |  |  |  |     path.setFillType(to_skia_path_fill_type(command.winding_rule)); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     auto paint = paint_style_to_skia_paint(*command.paint_style, command.bounding_rect().to_type<float>()); | 
					
						
							|  |  |  |  |     paint.setAntiAlias(true); | 
					
						
							|  |  |  |  |     paint.setAlphaf(command.opacity); | 
					
						
							|  |  |  |  |     surface().canvas().drawPath(path, paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::stroke_path_using_color(StrokePathUsingColor const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-01 17:27:22 +02:00
										 |  |  |  |     // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
 | 
					
						
							|  |  |  |  |     if (!command.thickness) | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2024-07-01 17:27:22 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-21 15:09:31 -05:00
										 |  |  |  |     // FIXME: Use .cap_style, .join_style, .miter_limit, .dash_array, .dash_offset.
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setAntiAlias(true); | 
					
						
							|  |  |  |  |     paint.setStyle(SkPaint::kStroke_Style); | 
					
						
							|  |  |  |  |     paint.setStrokeWidth(command.thickness); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							|  |  |  |  |     auto path = to_skia_path(command.path); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     path.offset(command.aa_translation.x(), command.aa_translation.y()); | 
					
						
							|  |  |  |  |     canvas.drawPath(path, paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::stroke_path_using_paint_style(StrokePathUsingPaintStyle const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-01 17:27:22 +02:00
										 |  |  |  |     // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
 | 
					
						
							|  |  |  |  |     if (!command.thickness) | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2024-07-01 17:27:22 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-21 15:09:31 -05:00
										 |  |  |  |     // FIXME: Use .cap_style, .join_style, .miter_limit, .dash_array, .dash_offset.
 | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto path = to_skia_path(command.path); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     path.offset(command.aa_translation.x(), command.aa_translation.y()); | 
					
						
							|  |  |  |  |     auto paint = paint_style_to_skia_paint(*command.paint_style, command.bounding_rect().to_type<float>()); | 
					
						
							|  |  |  |  |     paint.setAntiAlias(true); | 
					
						
							|  |  |  |  |     paint.setAlphaf(command.opacity); | 
					
						
							|  |  |  |  |     paint.setStyle(SkPaint::Style::kStroke_Style); | 
					
						
							|  |  |  |  |     paint.setStrokeWidth(command.thickness); | 
					
						
							|  |  |  |  |     surface().canvas().drawPath(path, paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::draw_ellipse(DrawEllipse const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-01 17:27:22 +02:00
										 |  |  |  |     // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
 | 
					
						
							|  |  |  |  |     if (!command.thickness) | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2024-07-01 17:27:22 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     auto const& rect = command.rect; | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setAntiAlias(true); | 
					
						
							|  |  |  |  |     paint.setStyle(SkPaint::kStroke_Style); | 
					
						
							|  |  |  |  |     paint.setStrokeWidth(command.thickness); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							|  |  |  |  |     canvas.drawOval(to_skia_rect(rect), paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::fill_ellipse(FillEllipse const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto const& rect = command.rect; | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							|  |  |  |  |     canvas.drawOval(to_skia_rect(rect), paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::draw_line(DrawLine const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-01 17:27:22 +02:00
										 |  |  |  |     // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
 | 
					
						
							|  |  |  |  |     if (!command.thickness) | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2024-07-01 17:27:22 +02:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-11 16:03:10 +03:00
										 |  |  |  |     auto from = to_skia_point(command.from); | 
					
						
							|  |  |  |  |     auto to = to_skia_point(command.to); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							| 
									
										
										
										
											2024-07-12 13:52:21 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2024-07-19 14:57:09 +02:00
										 |  |  |  |     paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     paint.setStrokeWidth(command.thickness); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							| 
									
										
										
										
											2024-07-12 13:52:21 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     switch (command.style) { | 
					
						
							|  |  |  |  |     case Gfx::LineStyle::Solid: | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  |     case Gfx::LineStyle::Dotted: { | 
					
						
							|  |  |  |  |         auto length = command.to.distance_from(command.from); | 
					
						
							|  |  |  |  |         auto dot_count = floor(length / (static_cast<float>(command.thickness) * 2)); | 
					
						
							|  |  |  |  |         auto interval = length / dot_count; | 
					
						
							|  |  |  |  |         SkScalar intervals[] = { 0, interval }; | 
					
						
							|  |  |  |  |         paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); | 
					
						
							|  |  |  |  |         paint.setStrokeCap(SkPaint::Cap::kRound_Cap); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // NOTE: As Skia doesn't render a dot exactly at the end of a line, we need
 | 
					
						
							|  |  |  |  |         //       to extend it by less then an interval.
 | 
					
						
							|  |  |  |  |         auto direction = to - from; | 
					
						
							|  |  |  |  |         direction.normalize(); | 
					
						
							|  |  |  |  |         to += direction * (interval / 2.0f); | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     case Gfx::LineStyle::Dashed: { | 
					
						
							|  |  |  |  |         auto length = command.to.distance_from(command.from) + command.thickness; | 
					
						
							|  |  |  |  |         auto dash_count = floor(length / static_cast<float>(command.thickness) / 4) * 2 + 1; | 
					
						
							|  |  |  |  |         auto interval = length / dash_count; | 
					
						
							|  |  |  |  |         SkScalar intervals[] = { interval, interval }; | 
					
						
							|  |  |  |  |         paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         auto direction = to - from; | 
					
						
							|  |  |  |  |         direction.normalize(); | 
					
						
							|  |  |  |  |         from -= direction * (command.thickness / 2.0f); | 
					
						
							|  |  |  |  |         to += direction * (command.thickness / 2.0f); | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     canvas.drawLine(from, to, paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::apply_backdrop_filter(ApplyBackdropFilter const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto rect = to_skia_rect(command.backdrop_region); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     canvas.save(); | 
					
						
							|  |  |  |  |     canvas.clipRect(rect); | 
					
						
							|  |  |  |  |     ScopeGuard guard = [&] { canvas.restore(); }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-18 11:34:25 +01:00
										 |  |  |  |     for (auto const& filter : command.backdrop_filter) { | 
					
						
							|  |  |  |  |         auto image_filter = to_skia_image_filter(filter); | 
					
						
							| 
									
										
										
										
											2024-10-25 15:37:21 +02:00
										 |  |  |  |         canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, nullptr, image_filter.get(), 0)); | 
					
						
							|  |  |  |  |         canvas.restore(); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::draw_rect(DrawRect const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     auto const& rect = command.rect; | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2024-07-19 14:57:09 +02:00
										 |  |  |  |     paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     paint.setStyle(SkPaint::kStroke_Style); | 
					
						
							|  |  |  |  |     paint.setStrokeWidth(1); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							|  |  |  |  |     canvas.drawRect(to_skia_rect(rect), paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::paint_radial_gradient(PaintRadialGradient const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |     auto const& radial_gradient_data = command.radial_gradient_data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-15 16:16:06 +03:00
										 |  |  |  |     auto color_stop_list = radial_gradient_data.color_stops.list; | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |     VERIFY(!color_stop_list.is_empty()); | 
					
						
							| 
									
										
										
										
											2024-07-15 16:16:06 +03:00
										 |  |  |  |     auto const& repeat_length = radial_gradient_data.color_stops.repeat_length; | 
					
						
							|  |  |  |  |     if (repeat_length.has_value()) | 
					
						
							|  |  |  |  |         color_stop_list = expand_repeat_length(color_stop_list, repeat_length.value()); | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto stops_with_replaced_transition_hints = replace_transition_hints_with_normal_color_stops(color_stop_list); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |     Vector<SkColor4f> colors; | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     Vector<SkScalar> positions; | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |     for (size_t stop_index = 0; stop_index < stops_with_replaced_transition_hints.size(); stop_index++) { | 
					
						
							|  |  |  |  |         auto const& stop = stops_with_replaced_transition_hints[stop_index]; | 
					
						
							|  |  |  |  |         if (stop_index > 0 && stop == stops_with_replaced_transition_hints[stop_index - 1]) | 
					
						
							|  |  |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |         colors.append(to_skia_color4f(stop.color)); | 
					
						
							| 
									
										
										
										
											2024-07-10 15:54:49 +02:00
										 |  |  |  |         positions.append(stop.position); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto const& rect = command.rect; | 
					
						
							| 
									
										
										
										
											2024-07-11 16:13:09 +03:00
										 |  |  |  |     auto center = to_skia_point(command.center.translated(command.rect.location())); | 
					
						
							| 
									
										
										
										
											2024-07-15 11:53:45 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto const size = command.size.to_type<float>(); | 
					
						
							|  |  |  |  |     SkMatrix matrix; | 
					
						
							|  |  |  |  |     // Skia does not support specifying of horizontal and vertical radius's separately,
 | 
					
						
							|  |  |  |  |     // so instead we apply scale matrix
 | 
					
						
							|  |  |  |  |     matrix.setScale(size.width() / size.height(), 1.0f, center.x(), center.y()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-16 16:43:11 +03:00
										 |  |  |  |     SkTileMode tile_mode = SkTileMode::kClamp; | 
					
						
							|  |  |  |  |     if (repeat_length.has_value()) | 
					
						
							|  |  |  |  |         tile_mode = SkTileMode::kRepeat; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |     auto color_space = SkColorSpace::MakeSRGB(); | 
					
						
							|  |  |  |  |     SkGradientShader::Interpolation interpolation = {}; | 
					
						
							|  |  |  |  |     interpolation.fColorSpace = SkGradientShader::Interpolation::ColorSpace::kSRGB; | 
					
						
							|  |  |  |  |     interpolation.fInPremul = SkGradientShader::Interpolation::InPremul::kYes; | 
					
						
							|  |  |  |  |     auto shader = SkGradientShader::MakeRadial(center, size.height(), colors.data(), color_space, positions.data(), positions.size(), tile_mode, interpolation, &matrix); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2024-07-19 14:57:09 +02:00
										 |  |  |  |     paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     paint.setShader(shader); | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     surface().canvas().drawRect(to_skia_rect(rect), paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-24 13:55:15 +03:00
										 |  |  |  | void DisplayListPlayerSkia::paint_conic_gradient(PaintConicGradient const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-11 15:51:41 +03:00
										 |  |  |  |     auto const& conic_gradient_data = command.conic_gradient_data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-15 14:52:47 +03:00
										 |  |  |  |     auto color_stop_list = conic_gradient_data.color_stops.list; | 
					
						
							|  |  |  |  |     auto const& repeat_length = conic_gradient_data.color_stops.repeat_length; | 
					
						
							|  |  |  |  |     if (repeat_length.has_value()) | 
					
						
							| 
									
										
										
										
											2024-07-15 16:16:06 +03:00
										 |  |  |  |         color_stop_list = expand_repeat_length(color_stop_list, repeat_length.value()); | 
					
						
							| 
									
										
										
										
											2024-07-15 14:52:47 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-11 15:51:41 +03:00
										 |  |  |  |     VERIFY(!color_stop_list.is_empty()); | 
					
						
							| 
									
										
										
										
											2024-07-15 13:44:57 +03:00
										 |  |  |  |     auto stops_with_replaced_transition_hints = replace_transition_hints_with_normal_color_stops(color_stop_list); | 
					
						
							| 
									
										
										
										
											2024-07-11 15:51:41 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |     Vector<SkColor4f> colors; | 
					
						
							| 
									
										
										
										
											2024-07-11 15:51:41 +03:00
										 |  |  |  |     Vector<SkScalar> positions; | 
					
						
							| 
									
										
										
										
											2024-07-15 13:44:57 +03:00
										 |  |  |  |     for (size_t stop_index = 0; stop_index < stops_with_replaced_transition_hints.size(); stop_index++) { | 
					
						
							|  |  |  |  |         auto const& stop = stops_with_replaced_transition_hints[stop_index]; | 
					
						
							|  |  |  |  |         if (stop_index > 0 && stop == stops_with_replaced_transition_hints[stop_index - 1]) | 
					
						
							|  |  |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |         colors.append(to_skia_color4f(stop.color)); | 
					
						
							| 
									
										
										
										
											2024-07-11 15:51:41 +03:00
										 |  |  |  |         positions.append(stop.position); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto const& rect = command.rect; | 
					
						
							| 
									
										
										
										
											2024-07-15 13:50:54 +03:00
										 |  |  |  |     auto center = command.position.translated(rect.location()).to_type<float>(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkMatrix matrix; | 
					
						
							| 
									
										
										
										
											2024-07-15 13:57:04 +03:00
										 |  |  |  |     matrix.setRotate(-90 + conic_gradient_data.start_angle, center.x(), center.y()); | 
					
						
							| 
									
										
										
										
											2024-07-18 14:05:30 +03:00
										 |  |  |  |     auto color_space = SkColorSpace::MakeSRGB(); | 
					
						
							|  |  |  |  |     SkGradientShader::Interpolation interpolation = {}; | 
					
						
							|  |  |  |  |     interpolation.fColorSpace = SkGradientShader::Interpolation::ColorSpace::kSRGB; | 
					
						
							|  |  |  |  |     interpolation.fInPremul = SkGradientShader::Interpolation::InPremul::kYes; | 
					
						
							|  |  |  |  |     auto shader = SkGradientShader::MakeSweep(center.x(), center.y(), colors.data(), color_space, positions.data(), positions.size(), SkTileMode::kRepeat, 0, 360, interpolation, &matrix); | 
					
						
							| 
									
										
										
										
											2024-07-11 15:51:41 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							| 
									
										
										
										
											2024-07-19 14:57:09 +02:00
										 |  |  |  |     paint.setAntiAlias(true); | 
					
						
							| 
									
										
										
										
											2024-07-11 15:51:41 +03:00
										 |  |  |  |     paint.setShader(shader); | 
					
						
							|  |  |  |  |     surface().canvas().drawRect(to_skia_rect(rect), paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-27 19:23:27 +00:00
										 |  |  |  | void DisplayListPlayerSkia::draw_triangle_wave(DrawTriangleWave const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-02-27 19:23:27 +00:00
										 |  |  |  |     // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
 | 
					
						
							|  |  |  |  |     if (!command.thickness) | 
					
						
							|  |  |  |  |         return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // FIXME: Support more than horizontal waves
 | 
					
						
							|  |  |  |  |     if (command.p1.y() != command.p2.y()) { | 
					
						
							|  |  |  |  |         dbgln("FIXME: Support more than horizontal waves"); | 
					
						
							|  |  |  |  |         return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     auto from = to_skia_point(command.p1); | 
					
						
							|  |  |  |  |     auto to = to_skia_point(command.p2); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setAntiAlias(true); | 
					
						
							|  |  |  |  |     paint.setStyle(SkPaint::kStroke_Style); | 
					
						
							|  |  |  |  |     paint.setStrokeWidth(command.thickness); | 
					
						
							|  |  |  |  |     paint.setStrokeJoin(SkPaint::kRound_Join); | 
					
						
							|  |  |  |  |     paint.setStrokeCap(SkPaint::kRound_Cap); | 
					
						
							|  |  |  |  |     paint.setColor(to_skia_color(command.color)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPath path; | 
					
						
							|  |  |  |  |     path.moveTo(from); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     float const wavelength = command.amplitude * 2.0f; | 
					
						
							|  |  |  |  |     float const half_wavelength = command.amplitude; | 
					
						
							|  |  |  |  |     float const quarter_wavelength = command.amplitude / 2.0f; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto position = from; | 
					
						
							|  |  |  |  |     auto remaining = abs(to.x() - position.x()); | 
					
						
							|  |  |  |  |     while (remaining > wavelength) { | 
					
						
							|  |  |  |  |         // Draw a whole wave
 | 
					
						
							|  |  |  |  |         path.lineTo(position.x() + quarter_wavelength, position.y() - quarter_wavelength); | 
					
						
							|  |  |  |  |         path.lineTo(position.x() + quarter_wavelength + half_wavelength, position.y() + quarter_wavelength); | 
					
						
							|  |  |  |  |         path.lineTo(position.x() + wavelength, position.y()); | 
					
						
							|  |  |  |  |         position.offset(wavelength, 0); | 
					
						
							|  |  |  |  |         remaining = abs(to.x() - position.x()); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Up
 | 
					
						
							|  |  |  |  |     if (remaining > quarter_wavelength) { | 
					
						
							|  |  |  |  |         path.lineTo(position.x() + quarter_wavelength, position.y() - quarter_wavelength); | 
					
						
							|  |  |  |  |         position.offset(quarter_wavelength, 0); | 
					
						
							|  |  |  |  |         remaining = abs(to.x() - position.x()); | 
					
						
							|  |  |  |  |     } else if (remaining >= 1) { | 
					
						
							|  |  |  |  |         auto fraction = remaining / quarter_wavelength; | 
					
						
							|  |  |  |  |         path.lineTo(position.x() + (fraction * quarter_wavelength), position.y() - (fraction * quarter_wavelength)); | 
					
						
							|  |  |  |  |         remaining = 0; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Down
 | 
					
						
							|  |  |  |  |     if (remaining > half_wavelength) { | 
					
						
							|  |  |  |  |         path.lineTo(position.x() + half_wavelength, position.y() + quarter_wavelength); | 
					
						
							|  |  |  |  |         position.offset(half_wavelength, 0); | 
					
						
							|  |  |  |  |         remaining = abs(to.x() - position.x()); | 
					
						
							|  |  |  |  |     } else if (remaining >= 1) { | 
					
						
							|  |  |  |  |         auto fraction = remaining / half_wavelength; | 
					
						
							|  |  |  |  |         path.lineTo(position.x() + (fraction * half_wavelength), position.y() - quarter_wavelength + (fraction * half_wavelength)); | 
					
						
							|  |  |  |  |         remaining = 0; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Back to middle
 | 
					
						
							|  |  |  |  |     if (remaining >= 1) { | 
					
						
							|  |  |  |  |         auto fraction = remaining / quarter_wavelength; | 
					
						
							|  |  |  |  |         path.lineTo(position.x() + (fraction * quarter_wavelength), position.y() + ((1 - fraction) * quarter_wavelength)); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     canvas.drawPath(path, paint); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-29 14:13:34 +03:00
										 |  |  |  | void DisplayListPlayerSkia::add_rounded_rect_clip(AddRoundedRectClip const& command) | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-06-19 03:29:10 +03:00
										 |  |  |  |     auto rounded_rect = to_skia_rrect(command.border_rect, command.corner_radii); | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     auto clip_op = command.corner_clip == CornerClip::Inside ? SkClipOp::kDifference : SkClipOp::kIntersect; | 
					
						
							|  |  |  |  |     canvas.clipRRect(rounded_rect, clip_op, true); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-06 15:26:47 +03:00
										 |  |  |  | void DisplayListPlayerSkia::add_mask(AddMask const& command) | 
					
						
							| 
									
										
										
										
											2024-07-24 22:03:19 +03:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-08-06 15:26:47 +03:00
										 |  |  |  |     auto const& rect = command.rect; | 
					
						
							| 
									
										
										
										
											2024-08-06 22:28:08 +03:00
										 |  |  |  |     if (rect.is_empty()) | 
					
						
							|  |  |  |  |         return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-25 15:42:15 +02:00
										 |  |  |  |     auto mask_surface = Gfx::PaintingSurface::create_with_size(m_context, rect.size(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied); | 
					
						
							| 
									
										
										
										
											2024-07-24 22:03:19 +03:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-29 10:24:57 +01:00
										 |  |  |  |     NonnullRefPtr old_surface = surface(); | 
					
						
							|  |  |  |  |     set_surface(mask_surface); | 
					
						
							| 
									
										
										
										
											2024-08-06 15:26:47 +03:00
										 |  |  |  |     execute(*command.display_list); | 
					
						
							| 
									
										
										
										
											2025-01-29 10:24:57 +01:00
										 |  |  |  |     set_surface(old_surface); | 
					
						
							| 
									
										
										
										
											2024-07-24 22:03:19 +03:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkMatrix mask_matrix; | 
					
						
							|  |  |  |  |     mask_matrix.setTranslate(rect.x(), rect.y()); | 
					
						
							| 
									
										
										
										
											2024-09-25 15:42:15 +02:00
										 |  |  |  |     auto image = mask_surface->sk_surface().makeImageSnapshot(); | 
					
						
							| 
									
										
										
										
											2024-07-24 22:03:19 +03:00
										 |  |  |  |     auto shader = image->makeShader(SkSamplingOptions(), mask_matrix); | 
					
						
							|  |  |  |  |     surface().canvas().clipShader(shader); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-08 14:17:54 +03:00
										 |  |  |  | void DisplayListPlayerSkia::paint_nested_display_list(PaintNestedDisplayList const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     canvas.translate(command.rect.x(), command.rect.y()); | 
					
						
							|  |  |  |  |     execute(*command.display_list); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-17 18:56:56 +02:00
										 |  |  |  | void DisplayListPlayerSkia::paint_scrollbar(PaintScrollBar const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto rect = to_skia_rect(command.rect); | 
					
						
							|  |  |  |  |     auto radius = rect.width() / 2; | 
					
						
							|  |  |  |  |     auto rrect = SkRRect::MakeRectXY(rect, radius, radius); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto fill_color = Color(Color::NamedColor::DarkGray).with_alpha(128); | 
					
						
							|  |  |  |  |     SkPaint fill_paint; | 
					
						
							|  |  |  |  |     fill_paint.setColor(to_skia_color(fill_color)); | 
					
						
							|  |  |  |  |     canvas.drawRRect(rrect, fill_paint); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto stroke_color = Color(Color::NamedColor::LightGray).with_alpha(128); | 
					
						
							|  |  |  |  |     SkPaint stroke_paint; | 
					
						
							|  |  |  |  |     stroke_paint.setStroke(true); | 
					
						
							|  |  |  |  |     stroke_paint.setStrokeWidth(1); | 
					
						
							|  |  |  |  |     stroke_paint.setColor(to_skia_color(stroke_color)); | 
					
						
							|  |  |  |  |     canvas.drawRRect(rrect, stroke_paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 02:57:57 +02:00
										 |  |  |  | void DisplayListPlayerSkia::apply_opacity(ApplyOpacity const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setAlphaf(command.opacity); | 
					
						
							|  |  |  |  |     canvas.saveLayer(nullptr, &paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-22 09:50:49 +01:00
										 |  |  |  | void DisplayListPlayerSkia::apply_composite_and_blending_operator(ApplyCompositeAndBlendingOperator const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setBlender(Gfx::to_skia_blender(command.compositing_and_blending_operator)); | 
					
						
							|  |  |  |  |     canvas.saveLayer(nullptr, &paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-22 19:53:59 +05:30
										 |  |  |  | void DisplayListPlayerSkia::apply_filters(ApplyFilters const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-12-18 11:34:25 +01:00
										 |  |  |  |     if (command.filter.is_empty()) { | 
					
						
							| 
									
										
										
										
											2024-11-22 19:53:59 +05:30
										 |  |  |  |         return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     sk_sp<SkImageFilter> image_filter; | 
					
						
							|  |  |  |  |     auto append_filter = [&image_filter](auto new_filter) { | 
					
						
							|  |  |  |  |         if (image_filter) | 
					
						
							|  |  |  |  |             image_filter = SkImageFilters::Compose(new_filter, image_filter); | 
					
						
							|  |  |  |  |         else | 
					
						
							|  |  |  |  |             image_filter = new_filter; | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // Apply filters in order
 | 
					
						
							| 
									
										
										
										
											2024-12-18 11:34:25 +01:00
										 |  |  |  |     for (auto filter : command.filter) { | 
					
						
							| 
									
										
										
										
											2024-11-22 19:53:59 +05:30
										 |  |  |  |         append_filter(to_skia_image_filter(filter)); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkPaint paint; | 
					
						
							|  |  |  |  |     paint.setImageFilter(image_filter); | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  |     canvas.saveLayer(nullptr, &paint); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-09 02:57:57 +02:00
										 |  |  |  | void DisplayListPlayerSkia::apply_transform(ApplyTransform const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto affine_transform = Gfx::extract_2d_affine_transform(command.matrix); | 
					
						
							|  |  |  |  |     auto new_transform = Gfx::AffineTransform {} | 
					
						
							|  |  |  |  |                              .translate(command.origin) | 
					
						
							|  |  |  |  |                              .multiply(affine_transform) | 
					
						
							|  |  |  |  |                              .translate(-command.origin); | 
					
						
							|  |  |  |  |     auto matrix = to_skia_matrix(new_transform); | 
					
						
							|  |  |  |  |     surface().canvas().concat(matrix); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | void DisplayListPlayerSkia::apply_mask_bitmap(ApplyMaskBitmap const& command) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     auto& canvas = surface().canvas(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-10 01:50:09 +01:00
										 |  |  |  |     auto const* mask_image = command.bitmap->sk_image(); | 
					
						
							| 
									
										
										
										
											2024-10-09 02:57:57 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     char const* sksl_shader = nullptr; | 
					
						
							|  |  |  |  |     if (command.kind == Gfx::Bitmap::MaskKind::Luminance) { | 
					
						
							|  |  |  |  |         sksl_shader = R"( | 
					
						
							|  |  |  |  |                 uniform shader mask_image; | 
					
						
							|  |  |  |  |                 half4 main(float2 coord) { | 
					
						
							|  |  |  |  |                     half4 color = mask_image.eval(coord); | 
					
						
							|  |  |  |  |                     half luminance = 0.2126 * color.b + 0.7152 * color.g + 0.0722 * color.r; | 
					
						
							|  |  |  |  |                     return half4(0.0, 0.0, 0.0, color.a * luminance); | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             )"; | 
					
						
							|  |  |  |  |     } else if (command.kind == Gfx::Bitmap::MaskKind::Alpha) { | 
					
						
							|  |  |  |  |         sksl_shader = R"( | 
					
						
							|  |  |  |  |                 uniform shader mask_image; | 
					
						
							|  |  |  |  |                 half4 main(float2 coord) { | 
					
						
							|  |  |  |  |                     half4 color = mask_image.eval(coord); | 
					
						
							|  |  |  |  |                     return half4(0.0, 0.0, 0.0, color.a); | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |             )"; | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(sksl_shader)); | 
					
						
							|  |  |  |  |     if (!effect) { | 
					
						
							|  |  |  |  |         dbgln("SkSL error: {}", error.c_str()); | 
					
						
							|  |  |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkMatrix mask_matrix; | 
					
						
							|  |  |  |  |     auto mask_position = command.origin; | 
					
						
							|  |  |  |  |     mask_matrix.setTranslate(mask_position.x(), mask_position.y()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     SkRuntimeShaderBuilder builder(effect); | 
					
						
							|  |  |  |  |     builder.child("mask_image") = mask_image->makeShader(SkSamplingOptions(), mask_matrix); | 
					
						
							|  |  |  |  |     canvas.clipShader(builder.makeShader()); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-06 15:26:47 +03:00
										 |  |  |  | bool DisplayListPlayerSkia::would_be_fully_clipped_by_painter(Gfx::IntRect rect) const | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |     return surface().canvas().quickReject(to_skia_rect(rect)); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-10 14:22:04 +03:00
										 |  |  |  | } |