$30 off During Our Annual Pro Sale. View Details »

Flutter CustomPaint @Flutter MeetUp(In Songdo) - 박제창

JaiChangPark
September 22, 2023

Flutter CustomPaint @Flutter MeetUp(In Songdo) - 박제창

https://festa.io/events/3887

Flutter CustomPaint @Flutter MeetUp(In Songdo) - 박제창

JaiChangPark

September 22, 2023
Tweet

More Decks by JaiChangPark

Other Decks in Programming

Transcript

  1. Flutter
    CustomPaint
    박제창(@Dreamwalker)
    1
    VOL. 1

    View Slide

  2. 박제창
    @Dreamwalker
    Flutter Seoul
    GDG Golang Korea
    2

    View Slide

  3. Agenda
    1
    2
    3
    4
    Intro
    What’s Skia
    CustomPainter & CustomPaint
    How to render Text Widget
    3

    View Slide

  4. CustomPaint
    CustomPainer
    사용하시나요?
    4

    View Slide

  5. 그렇다면 우리는
    CustomPaint를
    왜 ? 사용하게 될까요?
    5

    View Slide

  6. Rendering
    6

    View Slide

  7. Rendering
    The engine is responsible for rasterizing composited scenes
    whenever a new frame needs to be painted.
    It provides the low-level implementation of Flutter’s core API,
    including graphics (through Impeller on iOS and coming to
    Android, and Skia on other platforms) text layout.
    7

    View Slide

  8. Skia is an open source 2D graphics library which provides common APIs that work
    across a variety of hardware and software platforms.
    It serves as the graphics engine for Google Chrome and ChromeOS, Android, Flutter,
    and many other products.
    https://github.com/google/skia
    Skia
    The 2D Graphics Library
    8

    View Slide

  9. ● Predictable performance: Impeller compiles all shaders and reflection offline at build time.
    It builds all pipeline state objects upfront. The engine controls caching and caches explicitly.
    ● Instrumentable: Impeller tags and labels all graphics resources like textures, and buffers. It can
    capture and persist animations to disk without affecting per-frame rendering performance.
    ● Portable: Flutter doesn’t tie Impeller to a specific client rendering API. You can author shaders
    once and convert them to backend-specific formats as necessary.
    ● Leverages modern graphics APIs: Impeller uses, but doesn’t depend on,
    features available in modern APIs like Metal and Vulkan.
    ● Leverages concurrency: Impeller can distribute single-frame workloads
    across multiple threads if necessary.
    Impeller
    Impeller provides a new rendering runtime for Flutter.
    9

    View Slide

  10. Material
    Material 3 is the latest version of Google’s open-source design system. Design and
    build beautiful, usable products with Material 3.
    10

    View Slide

  11. Cupertino
    11
    Beautiful and high-fidelity widgets
    for current iOS design language.

    View Slide

  12. Custom UI
    12
    https://dribbble.com/shots/22568342-Football-NFT-App https://dribbble.com/shots/22556777-Intermittent-Fasting-Mobile-App-Design

    View Slide

  13. Skia
    The 2D Graphics Library
    13

    View Slide

  14. Skia
    14
    Platforms
    ● Windows 7, 8, 8.1, 10
    ● macOS 10.13 or later
    ● iOS 11 or later
    ● Android 4.1 (JellyBean) or later
    ● Ubuntu 18.04+, Debian 10+, openSUSE 15.2+, or
    Fedora Linux 32+

    View Slide

  15. Skia
    15

    View Slide

  16. 16
    SkCanvas

    View Slide

  17. void draw(SkCanvas* canvas) {
    SkPaint p;
    p.setColor(SK_ColorRED);
    p.setAntiAlias(true);
    p.setStyle(SkPaint::kStroke_Style);
    p.setStrokeWidth(10);
    canvas->drawLine(20, 20, 100, 100, p);
    }
    17

    View Slide

  18. @override
    void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
    ..color = Colors.red
    ..strokeWidth = 10;
    canvas.drawLine(
    const Offset(20, 20),
    const Offset(100, 100),
    paint,
    );
    }
    18

    View Slide

  19. Skia는 Flutter
    프레임워크에서 어디에
    들어가있는걸까요??
    19

    View Slide

  20. @override
    void drawLine(Offset p1, Offset p2, Paint paint) {
    assert(_offsetIsValid(p1));
    assert(_offsetIsValid(p2));
    _drawLine(p1.dx, p1.dy, p2.dx, p2.dy, paint._objects, paint._data);
    }
    @Native, Double, Double, Double, Double, Handle,
    Handle)>(symbol: 'Canvas::drawLine')
    external void _drawLine(double x1, double y1, double x2, double y2, List?
    paintObjects, ByteData paintData);
    20
    flutter/engine/lib/ui/painting.dart
    Flutter / ffi

    View Slide

  21. void Canvas::drawLine(double x1,
    double y1,
    double x2,
    double y2,
    Dart_Handle paint_objects,
    Dart_Handle paint_data) {
    Paint paint(paint_objects, paint_data);
    FML_DCHECK(paint.isNotNull());
    if (display_list_builder_) {
    DlPaint dl_paint;
    paint.paint(dl_paint, kDrawLineFlags);
    builder()->DrawLine(SkPoint::Make(SafeNarrow(x1), SafeNarrow(y1)),
    SkPoint::Make(SafeNarrow(x2), SafeNarrow(y2)),
    dl_paint);
    }
    }
    21
    /engine/lib/ui/painting/canvas.cc engine

    View Slide

  22. class DisplayListBuilder final : public virtual DlCanvas,
    public SkRefCnt,
    virtual DlOpReceiver,
    DisplayListOpFlags {
    // |DlCanvas|
    void DrawLine(const SkPoint& p0, const SkPoint& p1, const DlPaint& paint) override;
    void DrawCircle(const SkPoint& center, SkScalar radius,const DlPaint& paint) override;
    // |DlCanvas|
    void DrawRRect(const SkRRect& rrect, const DlPaint& paint) override;
    // |DlCanvas|
    void DrawDRRect(const SkRRect& outer, const SkRRect& inner, const DlPaint& paint)
    override;
    // |DlCanvas|
    void DrawPath(const SkPath& path, const DlPaint& paint) override;
    22
    /engine/display_list/dl_builder.h engine

    View Slide

  23. void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) {
    SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted();
    DisplayListAttributeFlags flags =
    (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags
    : kDrawHVLineFlags;
    OpResult result = PaintResult(current_, flags);
    if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
    Push(0, 1, p0, p1);
    CheckLayerOpacityCompatibility();
    UpdateLayerResult(result);
    }
    }
    void DisplayListBuilder::DrawLine(const SkPoint& p0,
    const SkPoint& p1,
    const DlPaint& paint) {
    SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags);
    drawLine(p0, p1);
    }
    23
    /engine/display_list/dl_builder.cc engine

    View Slide

  24. 24
    canvas.drawLine(
    const Offset(20, 20),
    const Offset(100, 100),
    paint,
    );
    Canvas
    _drawLine
    flutter/bin/cache/pkg/sky_engine/lib/ui/painting.dart

    CustomPainter
    Canvas::drawLine
    builder()->DrawLine(SkPoint::Make(SafeNarrow(x1),
    SafeNarrow(y1)), SkPoint::Make(SafeNarrow(x2),
    SafeNarrow(y2)), dl_paint);
    /engine/lib/ui/painting/canvas.cc /engine/display_list/dl_builder.cc
    DisplayListBuilder::drawLine
    SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY,
    p1.fX, p1.fY).makeSorted();
    /engine/display_list/dl_builder.cc
    CustomPaint Widget으로
    CustomPainter를 사용하여 선을
    그리는 과정

    View Slide

  25. CustomPainter
    CustomPaint
    25

    View Slide

  26. When asked to paint, CustomPaint first asks its painter to paint on the current canvas,
    then it paints its child, and then, after painting its child, it asks its foregroundPainter to
    paint. The coordinate system of the canvas matches the coordinate system of the
    CustomPaint object. The painters are expected to paint within a rectangle starting at the
    origin and encompassing a region of the given size. To enforce painting within those
    bounds, consider wrapping this CustomPaint with a ClipRect widget.
    Painters are implemented by subclassing CustomPainter.
    Custom painters normally size themselves to their child. If they do not have a child, they
    attempt to size themselves to the size, which defaults to Size.zero. size must not be null.
    A widget that provides a canvas on which to draw during the paint phase.
    CustomPaint
    26
    Widget

    View Slide

  27. CustomPaint(
    painter: Earth(),
    child: Text("Flutter"),
    )
    27
    CustomPaint

    View Slide

  28. class CustomPaint extends SingleChildRenderObjectWidget {
    const CustomPaint({
    super.key,
    this.painter,
    this.foregroundPainter,
    this.size = Size.zero,
    this.isComplex = false,
    this.willChange = false,
    super.child,
    })
    final CustomPainter? painter;
    28

    View Slide

  29. @override
    RenderCustomPaint createRenderObject(BuildContext context) {
    return RenderCustomPaint(
    painter: painter,
    foregroundPainter: foregroundPainter,
    preferredSize: size,
    isComplex: isComplex,
    willChange: willChange,
    );
    }
    CustomPainter? get painter => _painter;
    CustomPainter? _painter;
    29

    View Slide

  30. CustomPainter
    30
    class Earth extends CustomPainter {
    @override
    void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    }
    @override
    bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    throw UnimplementedError();
    }
    }

    View Slide

  31. CustomPainter
    31
    ● The paint method is called whenever the custom object needs to be repainted.
    ● The shouldRepaint method is called when a new instance of the class is provided, to
    check if the new instance actually represents different information.

    View Slide

  32. CustomClipper
    32
    class RoundClipper extends CustomClipper{
    @override
    getClip(Size size) {
    // TODO: implement getClip
    throw UnimplementedError();
    }
    @override
    bool shouldReclip(covariant CustomClipper oldClipper) {
    // TODO: implement shouldReclip
    throw UnimplementedError();
    }
    }

    View Slide

  33. CustomPainter & CustomClipper
    33
    https://pixabay.com/ko/photos/%EA%B3%B5%EC%98%88-%EB%8F%84%EC%98%88-%EB%8F%84%EC%9
    8%88%EA%B0%80-%EB%8F%84%EC%9E%90%EA%B8%B0-5276736/
    https://pixabay.com/ko/photos/%EC%9E%A5%EB%AF%B8-%EA%BD%83%EB%93%A4-%ED%99%94%EA%B0%80-%
    ED%8E%98%EC%9D%B8%ED%8A%B8-3411110/

    View Slide

  34. How to render
    Text Widget
    34

    View Slide

  35. ● StatelessWidget
    ● Text Widget
    ● RichText
    ● RenderParagraph
    ● TextPainter
    How to render Text Widget
    Flutter가 Text Widget을 랜더링 하는
    방식
    35

    View Slide

  36. @override
    Widget build(BuildContext context) {
    return Scaffold(
    appBar: AppBar(
    backgroundColor: Theme.of(context).colorScheme.inversePrimary,
    title: Text(widget.title),
    ),
    body: Center(
    const Text(
    'You have pushed the button this many times:',
    ),
    36
    Flutter

    View Slide

  37. class Text extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
    Widget result = RichText(
    textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
    textDirection: textDirection,
    locale: locale,
    softWrap: softWrap ?? defaultTextStyle.softWrap,
    overflow: overflow ?? effectiveTextStyle?.overflow ?? defaultTextStyle.overflow,
    ///..
    text: TextSpan(
    style: effectiveTextStyle,
    text: data,
    children: textSpan != null ? [textSpan!] : null,
    ),
    );
    37
    Flutter

    View Slide

  38. class RichText extends MultiChildRenderObjectWidget {
    @override
    RenderParagraph createRenderObject(BuildContext context) {
    assert(textDirection != null || debugCheckHasDirectionality(context));
    return RenderParagraph(text,
    textAlign: textAlign,
    textDirection: textDirection ?? Directionality.of(context),
    softWrap: softWrap,
    overflow: overflow,
    textScaler: textScaler,
    maxLines: maxLines,
    strutStyle: strutStyle,
    textWidthBasis: textWidthBasis,
    textHeightBehavior: textHeightBehavior,
    locale: locale ?? Localizations.maybeLocaleOf(context),
    registrar: selectionRegistrar,
    selectionColor: selectionColor,
    );
    }
    38
    Flutter

    View Slide

  39. /// A render object that displays a paragraph of text.
    class RenderParagraph extends RenderBox with ContainerRenderObjectMixinTextParentData>, RenderInlineChildrenContainerDefaults,
    RelayoutWhenSystemFontsChangeMixin {
    final TextPainter _textPainter;
    _textPainter = TextPainter(
    text: text,
    textAlign: textAlign,
    textDirection: textDirection,
    textScaler: textScaler == TextScaler.noScaling ?
    TextScaler.linear(textScaleFactor) : textScaler,
    maxLines: maxLines,
    ellipsis: overflow == TextOverflow.ellipsis ? _kEllipsis : null,
    locale: locale,
    strutStyle: strutStyle,
    textWidthBasis: textWidthBasis,
    textHeightBehavior: textHeightBehavior,
    )
    39
    Flutter

    View Slide

  40. class TextPainter {
    void paint(Canvas canvas, Offset offset) {
    final _TextPainterLayoutCacheWithOffset? layoutCache = _layoutCache;
    ///...
    canvas.drawParagraph(layoutCache.paragraph, offset +
    layoutCache.paintOffset);
    }
    40
    Flutter

    View Slide

  41. abstract class Canvas {
    factory Canvas(PictureRecorder recorder, [ Rect? cullRect ]) = _NativeCanvas;
    void drawParagraph(Paragraph paragraph, Offset offset);
    }
    41
    Flutter

    View Slide

  42. base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas {
    @override
    void drawParagraph(Paragraph paragraph, Offset offset) {
    final _NativeParagraph nativeParagraph = paragraph as _NativeParagraph;
    assert(!nativeParagraph.debugDisposed);
    assert(_offsetIsValid(offset));
    assert(!nativeParagraph._needsLayout);
    nativeParagraph._paint(this, offset.dx, offset.dy);
    }
    42
    Flutter

    View Slide

  43. // Redirecting the paint function in this way solves some dependency problems
    // in the C++ code. If we straighten out the C++ dependencies, we can remove
    // this indirection.
    @Native, Pointer, Double, Double)>
    (symbol: 'Paragraph::paint')
    external void _paint(_NativeCanvas canvas, double x, double y);
    43
    Flutter

    View Slide

  44. class SkCanvas;
    namespace txt {
    // Interface for text layout engines. The current implementation is based on
    // Skia's SkShaper/SkParagraph text layout module.
    class Paragraph {
    // Paints the laid out text onto the supplied DisplayListBuilder at
    // (x, y) offset from the origin. Only valid after Layout() is called.
    virtual bool Paint(flutter::DisplayListBuilder* builder,
    double x,
    double y) = 0;
    44
    /engine/third_party/txt/src/txt/paragraph.h
    engine

    View Slide

  45. // Implementation of Paragraph based on Skia's text layout module.
    class ParagraphSkia : public Paragraph {
    public:
    ParagraphSkia(std::unique_ptr paragraph,
    std::vector&& dl_paints,
    bool impeller_enabled);
    bool Paint(flutter::DisplayListBuilder* builder, double x, double y)
    override;
    private:
    TextStyle SkiaToTxt(const skia::textlayout::TextStyle& skia);
    std::unique_ptr paragraph_;
    45
    /engine/third_party/txt/src/skia/paragraph_skia.h
    engine

    View Slide

  46. bool ParagraphSkia::Paint(DisplayListBuilder* builder, double x, double y) {
    DisplayListParagraphPainter painter(builder, dl_paints_, impeller_enabled_);
    paragraph_->paint(&painter, x, y);
    return true;
    }
    46
    /engine/third_party/txt/src/skia/paragraph_skia.cc
    engine

    View Slide

  47. class DisplayListParagraphPainter : public skt::ParagraphPainter {
    public:
    DisplayListParagraphPainter(DisplayListBuilder* builder,
    const std::vector& dl_paints,
    bool impeller_enabled)
    : builder_(builder),
    dl_paints_(dl_paints),
    impeller_enabled_(impeller_enabled) {}
    47
    /engine/third_party/txt/src/skia/paragraph_skia.cc
    engine

    View Slide

  48. class ParagraphPainter {
    public:
    virtual void drawTextBlob(const sk_sp& blob, SkScalar x,
    SkScalar y, const SkPaintOrID& paint) = 0;
    virtual void drawTextShadow(const sk_sp& blob, SkScalar x,
    SkScalar y, SkColor color, SkScalar blurSigma) = 0;
    virtual void drawRect(const SkRect& rect, const SkPaintOrID& paint) = 0;
    virtual void drawFilledRect(const SkRect& rect, const DecorationStyle& decorStyle) = 0;
    virtual void drawPath(const SkPath& path, const DecorationStyle& decorStyle) = 0;
    virtual void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
    const DecorationStyle& decorStyle) = 0;
    virtual void clipRect(const SkRect& rect) = 0;
    virtual void translate(SkScalar dx, SkScalar dy) = 0;
    48
    skia/modules/skparagraph/include/ParagraphPainter.h
    Skia

    View Slide

  49. bool ParagraphSkia::Paint(DisplayListBuilder* builder, double x, double y) {
    DisplayListParagraphPainter painter(builder, dl_paints_, impeller_enabled_);
    paragraph_->paint(&painter, x, y);
    return true;
    }
    49
    /engine/third_party/txt/src/skia/paragraph_skia.cc
    engine

    View Slide

  50. namespace skia {
    namespace textlayout {
    class ParagraphPainter;
    class Paragraph {
    public:
    Paragraph(ParagraphStyle style, sk_sp fonts);
    virtual void paint(SkCanvas* canvas, SkScalar x, SkScalar y) = 0;
    virtual void paint(ParagraphPainter* painter, SkScalar x, SkScalar y) = 0;
    50
    /skia/modules/skparagraph/include/Paragraph.h
    skia

    View Slide

  51. class ParagraphImpl final : public Paragraph {
    public:
    void paint(SkCanvas* canvas, SkScalar x, SkScalar y) override;
    void paint(ParagraphPainter* canvas, SkScalar x, SkScalar y) override;
    }
    51
    /skia/modules/skparagraph/src/ParagraphImpl.h
    skia

    View Slide

  52. void ParagraphImpl::paint(SkCanvas* canvas, SkScalar x, SkScalar y) {
    CanvasParagraphPainter painter(canvas);
    paint(&painter, x, y);
    }
    void ParagraphImpl::paint(ParagraphPainter* painter, SkScalar x, SkScalar y) {
    for (auto& line : fLines) {
    line.paint(painter, x, y);
    }
    }
    52
    skia/modules/skparagraph/src/ParagraphImpl.cpp
    skia

    View Slide

  53. namespace skia {
    namespace textlayout {
    class ParagraphImpl;
    class TextLine {
    public:
    void paint(ParagraphPainter* painter, SkScalar x, SkScalar y);
    struct TextBlobRecord {
    void paint(ParagraphPainter* painter, SkScalar x, SkScalar y);
    sk_sp fBlob;
    SkPoint fOffset = SkPoint::Make(0.0f, 0.0f);
    ParagraphPainter::SkPaintOrID fPaint;
    SkRect fBounds = SkRect::MakeEmpty();
    bool fClippingNeeded = false;
    SkRect fClipRect = SkRect::MakeEmpty();
    // Extra fields only used for the (experimental) visitor 53
    /skia/modules/skparagraph/src/TextLine.h
    skia

    View Slide

  54. void TextLine::paint(ParagraphPainter* painter, SkScalar x, SkScalar y) {
    for (auto& record : fTextBlobCache) {
    record.paint(painter, x, y);
    }
    }
    void TextLine::TextBlobRecord::paint(ParagraphPainter* painter, SkScalar x,
    SkScalar y) {
    if (fClippingNeeded) {
    painter->save();
    painter->clipRect(fClipRect.makeOffset(x, y));
    }
    painter->drawTextBlob(fBlob, x + fOffset.x(), y + fOffset.y(), fPaint);
    if (fClippingNeeded) {
    painter->restore();
    }
    }
    54
    /skia/modules/skparagraph/src/TextLine.cpp
    skia

    View Slide

  55. class DisplayListParagraphPainter : public skt::ParagraphPainter {
    void drawTextBlob(const sk_sp& blob,
    SkScalar x,
    SkScalar y,
    const SkPaintOrID& paint) override {
    if (!blob) {
    return;
    }
    size_t paint_id = std::get(paint);
    FML_DCHECK(paint_id < dl_paints_.size());
    #ifdef IMPELLER_SUPPORTS_RENDERING
    /// code
    #endif // IMPELLER_SUPPORTS_RENDERING
    builder_->DrawTextBlob(blob, x, y, dl_paints_[paint_id]);
    }
    55
    /Users/jaichang/engine/third_party/txt/src/skia/paragraph_skia.cc skia
    engine

    View Slide

  56. class DisplayListParagraphPainter : public skt::ParagraphPainter {
    void drawTextBlob(const sk_sp& blob,
    SkScalar x,
    SkScalar y,
    const SkPaintOrID& paint) override {
    if (!blob) {
    return;
    }
    size_t paint_id = std::get(paint);
    FML_DCHECK(paint_id < dl_paints_.size());
    #ifdef IMPELLER_SUPPORTS_RENDERING
    /// code
    #endif // IMPELLER_SUPPORTS_RENDERING
    builder_->DrawTextBlob(blob, x, y, dl_paints_[paint_id]);
    }
    56
    /Users/jaichang/engine/third_party/txt/src/skia/paragraph_skia.cc skia
    engine

    View Slide

  57. #ifdef IMPELLER_SUPPORTS_RENDERING
    if (impeller_enabled_) {
    if (ShouldRenderAsPath(dl_paints_[paint_id])) {
    auto path = skia::textlayout::Paragraph::GetPath(blob.get());
    auto transformed = path.makeTransform(SkMatrix::Translate(
    x + blob->bounds().left(), y + blob->bounds().top()));
    builder_->DrawPath(transformed, dl_paints_[paint_id]);
    return;
    }
    builder_->DrawTextFrame(impeller::MakeTextFrameFromTextBlobSkia(blob), x,
    y, dl_paints_[paint_id]);
    return;
    }
    57
    /Users/jaichang/engine/third_party/txt/src/skia/paragraph_skia.cc skia
    engine

    View Slide

  58. namespace flutter {
    class DisplayListBuilder final :public virtual DlCanvas,
    public SkRefCnt,
    virtual DlOpReceiver,
    DisplayListOpFlags {
    // |DlCanvas|
    void DrawTextBlob(const sk_sp& blob,
    SkScalar x,
    SkScalar y,
    const DlPaint& paint) override;
    void drawTextFrame(const std::shared_ptr& text_frame,
    SkScalar x,
    SkScalar y) override;
    void DrawTextFrame(const std::shared_ptr& text_frame,
    SkScalar x,
    SkScalar y,
    const DlPaint& paint) override; 58
    engine/display_list/dl_builder.h skia
    engine

    View Slide

  59. void DisplayListBuilder::DrawTextBlob(const sk_sp& blob,
    SkScalar x,
    SkScalar y,
    const DlPaint& paint) {
    SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextBlobFlags);
    drawTextBlob(blob, x, y);
    }
    59
    /engine/display_list/dl_builder.cc skia
    engine

    View Slide

  60. void DisplayListBuilder::drawTextBlob(const sk_sp blob,
    SkScalar x,
    SkScalar y) {
    DisplayListAttributeFlags flags = kDrawTextBlobFlags;
    OpResult result = PaintResult(current_, flags);
    if (result == OpResult::kNoEffect) {
    return;
    }
    bool unclipped = AccumulateOpBounds(blob->bounds().makeOffset(x, y), flags);
    #if defined(OS_FUCHSIA)
    unclipped = true;
    #endif // OS_FUCHSIA
    if (unclipped) {
    Push(0, 1, blob, x, y);
    UpdateLayerOpacityCompatibility(false);
    UpdateLayerResult(result);
    }
    }
    60
    /engine/display_list/dl_builder.cc skia
    engine

    View Slide

  61. template
    void* DisplayListBuilder::Push(size_t pod, int render_op_inc, Args&&... args) {
    size_t size = SkAlignPtr(sizeof(T) + pod);
    FML_DCHECK(size < (1 << 24));
    if (used_ + size > allocated_) {
    static_assert(is_power_of_two(DL_BUILDER_PAGE),
    "This math needs updating for non-pow2.");
    // Next greater multiple of DL_BUILDER_PAGE.
    allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1);
    storage_.realloc(allocated_);
    FML_DCHECK(storage_.get());
    memset(storage_.get() + used_, 0, allocated_ - used_);
    }
    FML_DCHECK(used_ + size <= allocated_);
    auto op = reinterpret_cast(storage_.get() + used_);
    used_ += size;
    new (op) T{std::forward(args)...};
    op->type = T::kType;
    op->size = size;
    render_op_count_ += render_op_inc;
    op_index_++;
    return op + 1;
    } 61
    /engine/display_list/dl_builder.cc skia
    engine
    Push(0, 1, blob, x, y);

    View Slide

  62. // 4 byte header + 8 payload bytes + an aligned pointer take 24 bytes
    // (4 unused to align the pointer)
    struct DrawTextBlobOp final : DrawOpBase {
    static const auto kType = DisplayListOpType::kDrawTextBlob;
    DrawTextBlobOp(const sk_sp blob, SkScalar x, SkScalar y)
    : x(x), y(y), blob(std::move(blob)) {}
    const SkScalar x;
    const SkScalar y;
    const sk_sp blob;
    void dispatch(DispatchContext& ctx) const {
    if (op_needed(ctx)) {
    ctx.receiver.drawTextBlob(blob, x, y);
    }
    }
    };
    62
    /engine/display_list/dl_op_records.h skia
    engine
    Push(0, 1, blob, x, y);

    View Slide

  63. class DlOpReceiver {
    virtual void drawColor(DlColor color, DlBlendMode mode) = 0;
    virtual void drawPaint() = 0;
    virtual void drawLine(const SkPoint& p0, const SkPoint& p1) = 0;
    virtual void drawRect(const SkRect& rect) = 0;
    virtual void drawOval(const SkRect& bounds) = 0;
    virtual void drawCircle(const SkPoint& center, SkScalar radius) = 0;
    virtual void drawRRect(const SkRRect& rrect) = 0;
    virtual void drawDRRect(const SkRRect& outer, const SkRRect& inner) = 0;
    virtual void drawPath(const SkPath& path) = 0;
    virtual void drawTextBlob(const sk_sp blob,
    SkScalar x,
    SkScalar y) = 0;
    virtual void drawTextFrame(
    const std::shared_ptr& text_frame,
    SkScalar x,
    SkScalar y) = 0;
    63
    /engine/display_list/dl_op_records.h skia
    engine

    View Slide

  64. // A DisplayList can be read back by implementing the DlOpReceiver virtual
    // methods (with help from some of the classes in the utils file) and
    // passing an instance to the Dispatch() method, or it can be rendered
    // to Skia using a DlSkCanvasDispatcher.
    void DlSkCanvasDispatcher::drawTextBlob(const sk_sp blob,
    SkScalar x,
    SkScalar y) {
    canvas_->drawTextBlob(blob, x, y, paint());
    }
    64
    /engine/display_list/skia/dl_sk_dispatcher.cc skia
    engine
    private:
    SkCanvas* canvas_;

    View Slide

  65. void DlSkCanvasDispatcher::drawTextBlob(const sk_sp blob,
    SkScalar x,
    SkScalar y) {
    canvas_->drawTextBlob(blob, x, y, paint());
    }
    65
    /engine/display_list/skia/dl_sk_dispatcher.cc skia
    engine

    View Slide

  66. 66
    canvas_->drawTextBlob(blob, x, y, paint()); Skia API
    Graphic Library
    Backend Default OpenGL
    Vulkan
    ANGEL
    etc..
    GPU
    DRM, KMS etc Kernel
    Graphic
    Library
    Display
    Controller
    Screen

    View Slide

  67. 67
    double x1 = 10;
    double y1 = 10;
    double x2 = 20;
    double y2 = 20;
    x1 = 2*x1 / w - 1;
    y1 = 2*y1 / h - 1;
    x2 = 2*x2 / w - 1;
    y2 = 2*y2 / h - 1;
    glBegin(GL_LINES);
    glVertex2f(x1, y1);
    glVertex2f(x2, y2);
    glEnd();
    OpenGL 로 선을 그리고자 한다면

    View Slide

  68. 68
    Text( 'flutter',);
    RichTex†
    RenderParagraph
    TextPainter
    drawParagraph
    @Native
    (symbol: 'Paragraph::paint')
    ParagraphSkia
    ParagraphImpl
    DisplayListParagraphPainter
    DisplayListBuilder
    DlSkCanvasDispatcher
    impeller::DlDispatcher

    View Slide

  69. To be Continued
    69

    View Slide

  70. THANK
    YOU! v
    70
    박제창(@Dreamwalker)

    View Slide