/* * Copyright (c) 2024, Andreas Kling * Copyright (c) 2024-2025, Aliaksandr Kalenik * Copyright (c) 2024, Lucien Fiorini * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ #define AK_DONT_REPLACE_STD #define SK_SUPPORT_UNSPANNED_APIS #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Gfx { static sk_sp sk_image_for_decoded_image_frame(DecodedImageFrame const& frame) { return sk_image_from_bitmap(frame.bitmap(), frame.color_space()); } static void apply_paint_style(SkPaint& paint, PaintStyle const& style) { if (auto const& solid_color = as_if(style)) { paint.setColor(to_skia_color(solid_color->color())); } else if (auto const& linear_gradient = as_if(style)) { auto const& color_stops = linear_gradient->color_stops(); Vector colors; colors.ensure_capacity(color_stops.size()); Vector positions; positions.ensure_capacity(color_stops.size()); for (auto const& color_stop : color_stops) { colors.unchecked_append(to_skia_color(color_stop.color)); positions.unchecked_append(color_stop.position); } Array points { to_skia_point(linear_gradient->start_point()), to_skia_point(linear_gradient->end_point()) }; SkMatrix matrix; auto shader = SkGradientShader::MakeLinear(points.data(), colors.data(), positions.data(), color_stops.size(), SkTileMode::kClamp, 0, &matrix); paint.setShader(shader); } else if (auto const* radial_gradient = as_if(style)) { auto const& color_stops = radial_gradient->color_stops(); Vector colors; colors.ensure_capacity(color_stops.size()); Vector positions; positions.ensure_capacity(color_stops.size()); for (auto const& color_stop : color_stops) { colors.unchecked_append(to_skia_color(color_stop.color)); positions.unchecked_append(color_stop.position); } auto start_center = radial_gradient->start_center(); auto end_center = radial_gradient->end_center(); auto start_radius = radial_gradient->start_radius(); auto end_radius = radial_gradient->end_radius(); auto start_sk_point = to_skia_point(start_center); auto end_sk_point = to_skia_point(end_center); SkMatrix matrix; auto shader = SkGradientShader::MakeTwoPointConical(start_sk_point, start_radius, end_sk_point, end_radius, colors.data(), positions.data(), color_stops.size(), SkTileMode::kClamp, 0, &matrix); paint.setShader(shader); } else if (auto const* conic_gradient = as_if(style)) { auto const& color_stops = conic_gradient->color_stops(); Vector colors; colors.ensure_capacity(color_stops.size()); Vector positions; positions.ensure_capacity(color_stops.size()); for (auto const& color_stop : color_stops) { colors.unchecked_append(to_skia_color(color_stop.color)); positions.unchecked_append(color_stop.position); } auto center = conic_gradient->center(); auto start_angle_degrees = AK::to_degrees(conic_gradient->start_angle()); SkMatrix matrix; auto shader = SkGradientShader::MakeSweep(center.x(), center.y(), colors.data(), positions.data(), color_stops.size(), SkTileMode::kClamp, start_angle_degrees, start_angle_degrees + 360, 0, &matrix); paint.setShader(shader); } else if (auto const* canvas_pattern = as_if(style)) { auto frame = canvas_pattern->image(); if (!frame.has_value()) return; auto sk_image = sk_image_for_decoded_image_frame(*frame); if (!sk_image) return; auto repetition = canvas_pattern->repetition(); auto repeat_x = first_is_one_of(repetition, CanvasPatternPaintStyle::Repetition::Repeat, CanvasPatternPaintStyle::Repetition::RepeatX); auto repeat_y = first_is_one_of(repetition, CanvasPatternPaintStyle::Repetition::Repeat, CanvasPatternPaintStyle::Repetition::RepeatY); // FIXME: Implement sampling configuration. SkSamplingOptions sk_sampling_options { SkFilterMode::kLinear }; Optional transformation_matrix; if (canvas_pattern->transform().has_value()) { auto const& transform = canvas_pattern->transform().value(); transformation_matrix = SkMatrix::MakeAll( transform.a(), transform.c(), transform.e(), transform.b(), transform.d(), transform.f(), 0, 0, 1); } auto shader = sk_image->makeShader( repeat_x ? SkTileMode::kRepeat : SkTileMode::kDecal, repeat_y ? SkTileMode::kRepeat : SkTileMode::kDecal, sk_sampling_options, transformation_matrix.has_value() ? &transformation_matrix.value() : nullptr); paint.setShader(move(shader)); } else { dbgln("FIXME: Unsupported PaintStyle"); } } static void apply_filter(SkPaint& paint, Gfx::Filter const& filter) { paint.setImageFilter(to_skia_image_filter(filter)); } static SkPaint to_skia_paint(Gfx::PaintStyle const& style, Optional filter) { SkPaint paint; apply_paint_style(paint, style); if (filter.has_value()) apply_filter(paint, move(filter.value())); return paint; } PainterSkia::PainterSkia(NonnullRefPtr painting_surface) : m_painting_surface(move(painting_surface)) { m_initial_save_count = m_painting_surface->canvas().save(); } PainterSkia::~PainterSkia() = default; void PainterSkia::clear_rect(Gfx::FloatRect const& rect, Gfx::Color color) { auto& canvas = m_painting_surface->canvas(); canvas.save(); canvas.clipRect(to_skia_rect(rect)); canvas.clear(to_skia_color(color)); canvas.restore(); } void PainterSkia::fill_rect(Gfx::FloatRect const& rect, Color color) { SkPaint paint; paint.setColor(to_skia_color(color)); auto& canvas = m_painting_surface->canvas(); canvas.drawRect(to_skia_rect(rect), paint); } void PainterSkia::draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::DecodedImageFrame const& source, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, Optional filter, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) { SkPaint paint; if (filter.has_value()) apply_filter(paint, filter.value()); paint.setAlpha(static_cast(global_alpha * 255)); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); auto sk_image = sk_image_for_decoded_image_frame(source); if (!sk_image) return; auto& canvas = m_painting_surface->canvas(); canvas.drawImageRect( sk_image.get(), to_skia_rect(src_rect), to_skia_rect(dst_rect), to_skia_sampling_options(scaling_mode), &paint, SkCanvas::kStrict_SrcRectConstraint); } void PainterSkia::set_transform(Gfx::AffineTransform const& transform) { auto matrix = SkMatrix::MakeAll( transform.a(), transform.c(), transform.e(), transform.b(), transform.d(), transform.f(), 0, 0, 1); auto& canvas = m_painting_surface->canvas(); canvas.setMatrix(matrix); } void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thickness, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle cap_style, Gfx::Path::JoinStyle join_style, float miter_limit, Vector const& dash_array, float dash_offset) { // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing. if (thickness <= 0) return; SkPaint paint; paint.setAntiAlias(true); paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, blur_radius / 2)); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(thickness); paint.setColor(to_skia_color(color)); paint.setStrokeCap(to_skia_cap(cap_style)); paint.setStrokeJoin(to_skia_join(join_style)); paint.setStrokeMiter(miter_limit); paint.setPathEffect(SkDashPathEffect::Make(dash_array.data(), dash_array.size(), dash_offset)); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); auto sk_path = to_skia_path(path); auto& canvas = m_painting_surface->canvas(); canvas.drawPath(sk_path, paint); } void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, Optional filter, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const& cap_style, Gfx::Path::JoinStyle const& join_style, float miter_limit, Vector const& dash_array, float dash_offset) { // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing. if (thickness <= 0) return; auto sk_path = to_skia_path(path); auto paint = to_skia_paint(paint_style, filter); paint.setAntiAlias(true); float alpha = paint.getAlphaf(); paint.setAlphaf(alpha * global_alpha); paint.setStyle(SkPaint::Style::kStroke_Style); paint.setStrokeWidth(thickness); paint.setStrokeCap(to_skia_cap(cap_style)); paint.setStrokeJoin(to_skia_join(join_style)); paint.setStrokeMiter(miter_limit); paint.setPathEffect(SkDashPathEffect::Make(dash_array.data(), dash_array.size(), dash_offset)); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); auto& canvas = m_painting_surface->canvas(); canvas.drawPath(sk_path, paint); } void PainterSkia::fill_path(Gfx::Path const& path, Gfx::Color color, Gfx::WindingRule winding_rule, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) { SkPaint paint; paint.setAntiAlias(true); paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, blur_radius / 2)); paint.setColor(to_skia_color(color)); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); auto sk_path = to_skia_path(path); sk_path.setFillType(to_skia_path_fill_type(winding_rule)); auto& canvas = m_painting_surface->canvas(); canvas.drawPath(sk_path, paint); } void PainterSkia::fill_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, Optional filter, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::WindingRule winding_rule) { auto sk_path = to_skia_path(path); sk_path.setFillType(to_skia_path_fill_type(winding_rule)); auto paint = to_skia_paint(paint_style, filter); paint.setAntiAlias(true); float alpha = paint.getAlphaf(); paint.setAlphaf(alpha * global_alpha); paint.setBlender(to_skia_blender(compositing_and_blending_operator)); auto& canvas = m_painting_surface->canvas(); canvas.drawPath(sk_path, paint); } void PainterSkia::save() { auto& canvas = m_painting_surface->canvas(); canvas.save(); } void PainterSkia::restore() { auto& canvas = m_painting_surface->canvas(); canvas.restore(); } void PainterSkia::clip(Gfx::Path const& path, Gfx::WindingRule winding_rule) { auto sk_path = to_skia_path(path); sk_path.setFillType(to_skia_path_fill_type(winding_rule)); auto& canvas = m_painting_surface->canvas(); canvas.clipPath(sk_path, SkClipOp::kIntersect, true); } void PainterSkia::reset() { auto& canvas = m_painting_surface->canvas(); canvas.restoreToCount(m_initial_save_count); } }