123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- /*
- * Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
- * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #define AK_DONT_REPLACE_STD
- #include <AK/OwnPtr.h>
- #include <LibGfx/ImmutableBitmap.h>
- #include <LibGfx/PainterSkia.h>
- #include <LibGfx/PathSkia.h>
- #include <AK/TypeCasts.h>
- #include <core/SkCanvas.h>
- #include <core/SkPath.h>
- #include <effects/SkBlurMaskFilter.h>
- #include <effects/SkGradientShader.h>
- namespace Gfx {
- struct PainterSkia::Impl {
- RefPtr<Gfx::PaintingSurface> painting_surface;
- Impl(Gfx::PaintingSurface& surface)
- : painting_surface(surface)
- {
- }
- SkCanvas* canvas() const
- {
- return &painting_surface->canvas();
- }
- };
- static constexpr SkRect to_skia_rect(auto const& rect)
- {
- return SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height());
- }
- static constexpr SkColor to_skia_color(Gfx::Color const& color)
- {
- return SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue());
- }
- static SkPath to_skia_path(Gfx::Path const& path)
- {
- return static_cast<PathImplSkia const&>(path.impl()).sk_path();
- }
- static SkPathFillType to_skia_path_fill_type(Gfx::WindingRule winding_rule)
- {
- switch (winding_rule) {
- case Gfx::WindingRule::Nonzero:
- return SkPathFillType::kWinding;
- case Gfx::WindingRule::EvenOdd:
- return SkPathFillType::kEvenOdd;
- }
- VERIFY_NOT_REACHED();
- }
- PainterSkia::PainterSkia(NonnullRefPtr<Gfx::PaintingSurface> painting_surface)
- : m_impl(adopt_own(*new Impl { move(painting_surface) }))
- {
- }
- PainterSkia::~PainterSkia() = default;
- void PainterSkia::clear_rect(Gfx::FloatRect const& rect, Gfx::Color color)
- {
- SkPaint paint;
- paint.setColor(to_skia_color(color));
- paint.setBlendMode(SkBlendMode::kClear);
- impl().canvas()->drawRect(to_skia_rect(rect), paint);
- }
- void PainterSkia::fill_rect(Gfx::FloatRect const& rect, Color color)
- {
- SkPaint paint;
- paint.setColor(to_skia_color(color));
- impl().canvas()->drawRect(to_skia_rect(rect), paint);
- }
- static SkSamplingOptions to_skia_sampling_options(Gfx::ScalingMode scaling_mode)
- {
- switch (scaling_mode) {
- case Gfx::ScalingMode::NearestNeighbor:
- return SkSamplingOptions(SkFilterMode::kNearest);
- case Gfx::ScalingMode::BilinearBlend:
- case Gfx::ScalingMode::SmoothPixels:
- return SkSamplingOptions(SkFilterMode::kLinear);
- case Gfx::ScalingMode::BoxSampling:
- return SkSamplingOptions(SkCubicResampler::Mitchell());
- default:
- VERIFY_NOT_REACHED();
- }
- }
- void PainterSkia::draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, float global_alpha)
- {
- SkPaint paint;
- paint.setAlpha(static_cast<u8>(global_alpha * 255));
- impl().canvas()->drawImageRect(
- src_bitmap.sk_image(),
- 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);
- impl().canvas()->setMatrix(matrix);
- }
- static SkPoint to_skia_point(auto const& point)
- {
- return SkPoint::Make(point.x(), point.y());
- }
- static SkPaint to_skia_paint(Gfx::PaintStyle const& style)
- {
- if (is<Gfx::CanvasLinearGradientPaintStyle>(style)) {
- auto const& linear_gradient = static_cast<Gfx::CanvasLinearGradientPaintStyle const&>(style);
- auto const& color_stops = linear_gradient.color_stops();
- SkPaint paint;
- 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);
- }
- Array<SkPoint, 2> points;
- points[0] = to_skia_point(linear_gradient.start_point());
- points[1] = 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);
- return paint;
- }
- if (is<Gfx::CanvasRadialGradientPaintStyle>(style)) {
- auto const& radial_gradient = static_cast<Gfx::CanvasRadialGradientPaintStyle const&>(style);
- auto const& color_stops = radial_gradient.color_stops();
- SkPaint paint;
- 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);
- }
- 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);
- return paint;
- }
- return {};
- }
- void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thickness)
- {
- // 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.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(thickness);
- paint.setColor(to_skia_color(color));
- auto sk_path = to_skia_path(path);
- impl().canvas()->drawPath(sk_path, paint);
- }
- void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thickness, float blur_radius)
- {
- // 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));
- auto sk_path = to_skia_path(path);
- impl().canvas()->drawPath(sk_path, paint);
- }
- void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, float thickness, float global_alpha)
- {
- // 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);
- paint.setAntiAlias(true);
- paint.setAlphaf(global_alpha);
- paint.setStyle(SkPaint::Style::kStroke_Style);
- paint.setStrokeWidth(thickness);
- impl().canvas()->drawPath(sk_path, paint);
- }
- void PainterSkia::fill_path(Gfx::Path const& path, Gfx::Color color, Gfx::WindingRule winding_rule)
- {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(to_skia_color(color));
- auto sk_path = to_skia_path(path);
- sk_path.setFillType(to_skia_path_fill_type(winding_rule));
- impl().canvas()->drawPath(sk_path, paint);
- }
- void PainterSkia::fill_path(Gfx::Path const& path, Gfx::Color color, Gfx::WindingRule winding_rule, float blur_radius)
- {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, blur_radius / 2));
- paint.setColor(to_skia_color(color));
- auto sk_path = to_skia_path(path);
- sk_path.setFillType(to_skia_path_fill_type(winding_rule));
- impl().canvas()->drawPath(sk_path, paint);
- }
- void PainterSkia::fill_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, float global_alpha, 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);
- paint.setAntiAlias(true);
- paint.setAlphaf(global_alpha);
- impl().canvas()->drawPath(sk_path, paint);
- }
- void PainterSkia::save()
- {
- impl().canvas()->save();
- }
- void PainterSkia::restore()
- {
- impl().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));
- impl().canvas()->clipPath(sk_path, SkClipOp::kIntersect, true);
- }
- }
|