diff --git a/CHANGELOG.md b/CHANGELOG.md index bb4f8a2..8bcd5a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0 + +- **ADDED**: `RePaint` and `RePaint.inline` widget now has `repaintBoundary` property. + ## 0.0.8 - **FIXED**: Allow to select empty rect in `QuadTree` example. diff --git a/analysis_options.yaml b/analysis_options.yaml index 5c64795..71d18ae 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -128,7 +128,6 @@ linter: unnecessary_statements: true unnecessary_string_escapes: true unnecessary_string_interpolations: true - unsafe_html: true use_full_hex_values_for_flutter_colors: true use_raw_strings: true use_string_buffers: true @@ -183,7 +182,6 @@ linter: non_constant_identifier_names: true constant_identifier_names: true directives_ordering: true - package_api_docs: true implementation_imports: true prefer_interpolation_to_compose_strings: true unnecessary_brace_in_string_interps: true diff --git a/example/lib/src/feature/fps/fps_screen.dart b/example/lib/src/feature/fps/fps_screen.dart index cdb0936..8e07696 100644 --- a/example/lib/src/feature/fps/fps_screen.dart +++ b/example/lib/src/feature/fps/fps_screen.dart @@ -31,6 +31,7 @@ class _FpsScreenState extends State { Positioned.fill( child: RePaint.inline( frameRate: frameRate, + repaintBoundary: true, render: (box, state, canvas) { final now = DateTime.now(); if (now.second != second) { @@ -356,5 +357,4 @@ final class FrameRateGraph extends RePainterBase { canvas.restore(); } - } diff --git a/example/lib/src/feature/sunflower/sunflower_screen.dart b/example/lib/src/feature/sunflower/sunflower_screen.dart index ed15f3d..e7715e6 100644 --- a/example/lib/src/feature/sunflower/sunflower_screen.dart +++ b/example/lib/src/feature/sunflower/sunflower_screen.dart @@ -2,7 +2,7 @@ import 'dart:math' as math; import 'dart:typed_data'; -import 'dart:ui'; +import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:repaint/repaint.dart'; @@ -148,12 +148,12 @@ class SunflowerPainter extends PerformanceOverlayPainter { int max = 10000, }) : _maxSeeds = max, _positions = Float32List(max * 6), - _colors = Int32List(max * 3), + /* _colors = Int32List(max * 3), _vertices = Vertices.raw( VertexMode.triangles, Float32List(0), colors: Int32List(0), - ), + ), */ _theme = ThemeData.light() { _initVertices(); } @@ -193,18 +193,18 @@ class SunflowerPainter extends PerformanceOverlayPainter { ThemeData _theme; - final Float32List _positions; + Float32List _positions; - final Int32List _colors; + //final Int32List _colors; - Vertices _vertices; + //Vertices _vertices; void _initVertices() { - _vertices = Vertices.raw( + /* _vertices = Vertices.raw( VertexMode.triangles, _positions, colors: _colors, - ); + ); */ } /// Set the number of seeds in the sunflower @@ -215,7 +215,7 @@ class SunflowerPainter extends PerformanceOverlayPainter { /// Генерирует вершины равнобедренного треугольника, вписанного в окружность /// с радиусом [radius] и центром в точке [center]. - static List generateIsoscelesPoints( + /* static List generateIsoscelesPoints( Offset center, [ double radius = 6, ]) { @@ -232,7 +232,7 @@ class SunflowerPainter extends PerformanceOverlayPainter { result[y] = center.dy + radius * math.sin(angle); } return result; - } + } */ @override void internalUpdate(RePaintBox box, Duration elapsed, double delta) { @@ -240,6 +240,21 @@ class SunflowerPainter extends PerformanceOverlayPainter { final radius = size.shortestSide / 2; // Радиус окружности final center = size.center(Offset.zero); // Центр окружности + // Draw points + final f32l = _positions = Float32List(_maxSeeds * 2); + for (var i = 0; i < _maxSeeds; i++) { + final outer = i < _seeds; + // Центр треугольника + final Offset(:dx, :dy) = center + + (outer + ? _evalOuter(radius, _maxSeeds, i) + : _evalInner(radius, _maxSeeds, i)); + f32l + ..[i * 2 + 0] = dx + ..[i * 2 + 1] = dy; + } + + /* // https://github.com/flutter/flutter/issues/160184#issuecomment-2560184639 int toARGB32(Color color) { int floatToInt8(double x) => (x * 255.0).round() & 0xff; @@ -294,7 +309,7 @@ class SunflowerPainter extends PerformanceOverlayPainter { VertexMode.triangles, _positions, colors: _colors, - ); + ); */ } @override @@ -302,20 +317,44 @@ class SunflowerPainter extends PerformanceOverlayPainter { final canvas = context.canvas; var paint = Paint() ..style = PaintingStyle.fill - ..strokeWidth = 4 - ..isAntiAlias = false ..blendMode = BlendMode.src - ..filterQuality = FilterQuality.none; + ..filterQuality = FilterQuality.none + ..isAntiAlias = false; canvas.drawRect( Offset.zero & box.size, paint..color = _theme.canvasColor, ); - canvas.drawVertices( + paint = Paint() + ..strokeWidth = 8 + ..blendMode = BlendMode.src + ..filterQuality = FilterQuality.none + ..strokeCap = StrokeCap.round + ..color = _theme.primaryColor + ..isAntiAlias = false; + + // Draw the sunflower seeds points per batch of 5000. + final count = _maxSeeds; + const batch = 5000; + for (var offset = 0; offset < count; offset += batch) { + final start = offset; + final end = math.min(offset + batch, count); + final positionsView = + Float32List.sublistView(_positions, start * 2, end * 2); + canvas.drawRawPoints(ui.PointMode.points, positionsView, paint); + } + + /* canvas.drawRawPoints( + ui.PointMode.points, + _positions, + paint, + ); */ + + /* canvas.drawVertices( _vertices, BlendMode.src, paint, - ); + ); */ } } diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 5314c84..9d2eff6 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -15,8 +15,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 + url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 031c9b8..490d963 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -59,6 +59,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/lib/src/repaint.dart b/lib/src/repaint.dart index 4e6b127..de5c4e0 100644 --- a/lib/src/repaint.dart +++ b/lib/src/repaint.dart @@ -12,6 +12,7 @@ class RePaint extends LeafRenderObjectWidget { /// {@macro repaint} const RePaint({ required this.painter, + this.repaintBoundary = true, super.key, }); @@ -22,11 +23,14 @@ class RePaint extends LeafRenderObjectWidget { /// The [update] is called periodically by the loop. /// The [render] is called to render the scene after the update. /// The [tearDown] is called to unmount and dispose the controller. + /// The [repaintBoundary] is used to create a new layer for the scene. /// The [key] is used to identify the widget. /// /// After the [frameRate] is set, the real frame rate will be lower. /// Before the frame rate, updates are limited by the flutter ticker, /// so the resulting frame rate will be noticeably lower. + /// + /// By default, the [repaintBoundary] is set to false for [inline] widgets. /// {@macro repaint} static Widget inline({ required void Function(RePaintBox box, T state, Canvas canvas) render, @@ -34,6 +38,7 @@ class RePaint extends LeafRenderObjectWidget { T? Function(RePaintBox box, T state, double delta)? update, void Function(T state)? tearDown, int? frameRate, + bool repaintBoundary = false, Key? key, }) => RePaintInline( @@ -42,12 +47,23 @@ class RePaint extends LeafRenderObjectWidget { update: update, tearDown: tearDown, frameRate: frameRate, + repaintBoundary: repaintBoundary, key: key, ); - /// The painter controller. + /// The painter controller, used to update and paint the scene. + /// For example, a game controller or a custom painter. final RePainter painter; + /// Whether the controller should create a new layer for the scene. + /// If `true`, the controller will create a new layer for the scene. + /// If `false`, the controller will not create a new layer for the scene. + /// + /// This is useful when the controller needs to be repainted frequently + /// separately from the other widgets and the scene is complex + /// and has many layers. + final bool repaintBoundary; + @override RePaintElement createElement() => RePaintElement(this); @@ -55,11 +71,14 @@ class RePaint extends LeafRenderObjectWidget { RenderObject createRenderObject(BuildContext context) => RePaintBox( painter: painter, context: context, + isRepaintBoundary: repaintBoundary, ); @override void updateRenderObject(BuildContext context, RePaintBox renderObject) { - renderObject._context = context; + renderObject + .._context = context + ..isRepaintBoundary = repaintBoundary; if (identical(painter, renderObject.painter)) return; if (renderObject.attached) painter.unmount(); assert(renderObject.owner != null, 'RenderObject is not attached.'); @@ -109,8 +128,10 @@ class RePaintBox extends RenderBox with WidgetsBindingObserver { RePaintBox({ required RePainter painter, required BuildContext context, + required bool isRepaintBoundary, }) : _painter = painter, - _context = context; + _context = context, + _$isRepaintBoundary = isRepaintBoundary; /// Current controller. RePainter get painter => _painter; @@ -128,8 +149,17 @@ class RePaintBox extends RenderBox with WidgetsBindingObserver { Size get size => _size; Size _size = Size.zero; + bool _$isRepaintBoundary; + + // Change the repaint boundary flag. + set isRepaintBoundary(bool value) { + if (_$isRepaintBoundary == value) return; + _$isRepaintBoundary = value; + markNeedsCompositingBitsUpdate(); + } + @override - bool get isRepaintBoundary => true; + bool get isRepaintBoundary => _$isRepaintBoundary; @override bool get alwaysNeedsCompositing => false; diff --git a/lib/src/repaint_inline.dart b/lib/src/repaint_inline.dart index eb66a00..6d47e0e 100644 --- a/lib/src/repaint_inline.dart +++ b/lib/src/repaint_inline.dart @@ -15,6 +15,7 @@ class RePaintInline extends StatefulWidget { this.tearDown, this.frameRate, this.needsPaint = true, + this.repaintBoundary = false, super.key, }); @@ -41,6 +42,15 @@ class RePaintInline extends StatefulWidget { /// 120 - 120 frames per second. final int? frameRate; + /// Whether the controller should create a new layer for the scene. + /// If `true`, the controller will create a new layer for the scene. + /// If `false`, the controller will not create a new layer for the scene. + /// + /// This is useful when the controller needs to be repainted frequently + /// separately from the other widgets and the scene is complex + /// and has many layers. + final bool repaintBoundary; + /// The controller needs to be repainted after the update. /// /// If `true`, the controller will be repainted. @@ -80,7 +90,8 @@ class _RePaintInlineState extends State> { } @override - Widget build(BuildContext context) => RePaint(painter: painter); + Widget build(BuildContext context) => + RePaint(painter: painter, repaintBoundary: widget.repaintBoundary); } /// Internal controller for inline state. diff --git a/pubspec.yaml b/pubspec.yaml index cbb4e93..b7e88ea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ name: repaint description: > Library for creating and managing a canvas similar to CustomPaint but with more features. -version: 0.0.8 +version: 0.1.0 homepage: https://github.com/PlugFox/repaint @@ -50,7 +50,7 @@ dev_dependencies: sdk: flutter test: ^1.25.0 fake_async: ^1.3.0 - flutter_lints: '>=4.0.0 <6.0.0' + flutter_lints: ">=4.0.0 <6.0.0" benchmark_harness: ^2.3.1 flame: ^1.23.0 vector_math: ^2.1.4