Skip to content

Commit c6b7271

Browse files
committed
Add RasterTileLayerOptions.basePaint & .paintTile
Fix hairline cracks between tiles with default `Paint` Rename `RasterTileData` to `InternalRasterTileData` and make for internal purposes Create `RasterTileData` to expose specific information publicly previously exposed by now-`InternalRasterTileData`
1 parent 88887b6 commit c6b7271

File tree

8 files changed

+361
-164
lines changed

8 files changed

+361
-164
lines changed

example/lib/pages/home.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart' show kIsWeb;
22
import 'package:flutter/material.dart';
33
import 'package:flutter/scheduler.dart';
44
import 'package:flutter_map/flutter_map.dart';
5-
import 'package:flutter_map_example/misc/tile_providers.dart';
65
import 'package:flutter_map_example/widgets/drawer/floating_menu_button.dart';
76
import 'package:flutter_map_example/widgets/drawer/menu_drawer.dart';
87
import 'package:flutter_map_example/widgets/first_start_dialog.dart';
@@ -38,9 +37,15 @@ class _HomePageState extends State<HomePage> {
3837
initialZoom: 5,
3938
),
4039
children: [
41-
const RasterTileLayer.simple(
40+
RasterTileLayer.simple(
4241
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
4342
uaIdentifier: 'dev.fleaflet.flutter_map.demo',
43+
rasterOptions: RasterTileLayerOptions(
44+
paintTile: (canvas, size, tileCoordinates, tileData,
45+
tilePaint, drawImage) {
46+
drawImage();
47+
},
48+
),
4449
),
4550
RichAttributionWidget(
4651
popupInitialDisplayDuration: const Duration(seconds: 5),

lib/src/layer/modern_tile_layer/tile_layers/raster/options.dart

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,96 @@ import 'package:flutter_map/flutter_map.dart';
33

44
@immutable
55
class RasterTileLayerOptions {
6-
const RasterTileLayerOptions({this.crs});
6+
const RasterTileLayerOptions({
7+
this.crs,
8+
this.basePaint,
9+
this.paintTile,
10+
});
711

812
final Crs? crs;
913

14+
/// Optional custom [Paint] which is used (potentially with further
15+
/// modifications) to render tile raster images to the canvas.
16+
///
17+
/// It is recommended to set:
18+
/// - [Paint.filterQuality] to [FilterQuality.high] (to maximize image
19+
/// quality)
20+
/// - [Paint.isAntiAlias] to `false` (to avoid hairline fractures between
21+
/// tiles)
22+
///
23+
/// These are the default properties if unset.
24+
final Paint? basePaint;
25+
26+
/// Optional callback responsible for painting tiles onto the canvas.
27+
///
28+
/// If left unset, the painting algorithm is simple and uses the [basePaint]
29+
/// (if set, otherwise the default).
30+
///
31+
/// ---
32+
///
33+
/// This callback works by providing a sub-`canvas`, which has the `size` of
34+
/// the tile scaled for the current zoom level. When this `canvas` is applied
35+
/// to the layer canvas, it is positioned at the screen position of the tile.
36+
/// See [CustomPainter.paint] for more image about painting onto a canvas.
37+
///
38+
/// The tile image and any other custom drawings should be painted onto the
39+
/// provided `canvas`, usually without any overflow. The non-overflowing area
40+
/// `Rect` is given by:
41+
///
42+
/// ```dart
43+
/// final rect = Offset.zero & size;
44+
/// // To clip all operations to the non-overflowing area, if necessary:
45+
/// canvas.clipRect(rect);
46+
/// ```
47+
///
48+
/// `tileCoordinates` and `tileData` are information about the tile currently
49+
/// being painted.
50+
///
51+
/// `tilePaint` is initially the [basePaint] (if set, otherwise the default).
52+
/// `tilePaint` may be modified in the callback. If so, the modified paint
53+
/// is then reused for every subsequent paint. Therefore, it is important to
54+
/// re-modify any modified properties on every paint. Therefore, if only the
55+
/// same properties that [basePaint] sets are modified, then there is no use
56+
/// for [basePaint]. This behaviour is used to improve performance: only one
57+
/// [Paint] object is constructed by default. Note that any [Paint.color] set
58+
/// may have no effect, except for its [Color.a] field, which may be changed
59+
/// by the renderer.
60+
///
61+
/// `drawImage` should be called in the callback. It uses the `tilePaint` to
62+
/// draw the tile's image into the supplied argument `destRect` (the entire
63+
/// available tile `canvas` if unset).
64+
///
65+
/// For example, to shrink the tile image to leave a 20 logical pixel margin
66+
/// on the right and bottom sides:
67+
///
68+
/// ```dart
69+
/// paintTile: (canvas, size, tileCoordinates, tileData,
70+
/// tilePaint, drawImage) {
71+
/// drawImage(
72+
/// destRect: Offset.zero & ((size - Offset(20, 20)) as Size),
73+
/// );
74+
/// },
75+
/// ```
76+
///
77+
/// Only calling `drawImage` without any arguments is equivalent to not
78+
/// setting this callback.
79+
final void Function(
80+
Canvas canvas,
81+
Size size,
82+
TileCoordinates tileCoordinates,
83+
RasterTileData tileData,
84+
Paint tilePaint,
85+
void Function({Rect? destRect}) drawImage,
86+
)? paintTile;
87+
1088
@override
1189
bool operator ==(Object other) =>
1290
identical(this, other) ||
13-
(other is RasterTileLayerOptions && crs == other.crs);
91+
(other is RasterTileLayerOptions &&
92+
crs == other.crs &&
93+
basePaint == other.basePaint &&
94+
paintTile == other.paintTile);
1495

1596
@override
16-
int get hashCode => Object.hash(crs, null);
97+
int get hashCode => Object.hash(crs, basePaint, paintTile);
1798
}

lib/src/layer/modern_tile_layer/tile_layers/raster/renderer.dart

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ class _RasterRenderer extends StatefulWidget {
88
required this.rasterOptions,
99
}) : super(key: ValueKey(layerKey));
1010

11-
final Map<({TileCoordinates coordinates, Object layerKey}), RasterTileData>
12-
visibleTiles;
11+
final Map<({TileCoordinates coordinates, Object layerKey}),
12+
InternalRasterTileData> visibleTiles;
1313
final TileLayerOptions options;
1414
final RasterTileLayerOptions rasterOptions;
1515

@@ -46,6 +46,7 @@ class __RasterRendererState extends State<_RasterRenderer> {
4646
willChange: true,
4747
painter: _RasterPainter(
4848
options: widget.options,
49+
rasterOptions: widget.rasterOptions,
4950
visibleTiles: widget.visibleTiles.entries.map(
5051
(tile) => (
5152
coordinates: tile.key.coordinates,
@@ -57,9 +58,7 @@ class __RasterRendererState extends State<_RasterRenderer> {
5758
data: tile.value,
5859
),
5960
),
60-
//tiles: tiles..sort(renderOrder),
61-
//tilePaint: widget.tilePaint,
62-
//tileOverlayPainter: widget.tileOverlayPainter,
61+
// TODO: Really? No sorting?
6362
),
6463
),
6564
);
@@ -68,52 +67,87 @@ class __RasterRendererState extends State<_RasterRenderer> {
6867

6968
class _RasterPainter extends CustomPainter {
7069
final TileLayerOptions options;
70+
final RasterTileLayerOptions rasterOptions;
7171
final Iterable<
7272
({
7373
TileCoordinates coordinates,
7474
double scaledTileDimension,
7575
Offset currentPixelOrigin,
76-
RasterTileData data
76+
InternalRasterTileData data
7777
})> visibleTiles;
7878

7979
_RasterPainter({
8080
super.repaint,
8181
required this.options,
82+
required this.rasterOptions,
8283
required this.visibleTiles,
8384
});
8485

85-
final Paint _basePaint = (null ?? Paint())
86-
..filterQuality = FilterQuality.high
87-
..isAntiAlias = true;
86+
late final _paint = rasterOptions.basePaint ??
87+
(Paint()
88+
..filterQuality = FilterQuality.high
89+
..isAntiAlias = false);
8890

8991
@override
9092
void paint(Canvas canvas, Size size) {
9193
for (final tile in visibleTiles) {
92-
if (tile.data.loaded?.successfulImageInfo?.image case final image?) {
93-
final origin = Offset(
94-
tile.coordinates.x * tile.scaledTileDimension -
95-
tile.currentPixelOrigin.dx,
96-
tile.coordinates.y * tile.scaledTileDimension -
97-
tile.currentPixelOrigin.dy,
98-
);
99-
100-
//final paint = _basePaint
101-
// ..color = (null?.color.withOpacity(tile.tileImage.opacity) ??
102-
// Color.fromRGBO(0, 0, 0, tile.tileImage.opacity));
103-
104-
canvas.drawImageRect(
105-
image,
106-
Offset.zero &
107-
Size(image.width.toDouble(), image.height.toDouble()), // src
108-
origin & Size.square(tile.scaledTileDimension), // dest
109-
_basePaint,
110-
);
94+
final tileSize = Size.square(tile.scaledTileDimension);
95+
final originDx = tile.coordinates.x * tile.scaledTileDimension -
96+
tile.currentPixelOrigin.dx;
97+
final originDy = tile.coordinates.y * tile.scaledTileDimension -
98+
tile.currentPixelOrigin.dy;
99+
100+
if (rasterOptions.paintTile == null) {
101+
if (tile.data.imageInfo?.image case final image?) {
102+
//_basePaint..color = _basePaint.color.withAlpha(tile.data.opacity);
103+
104+
canvas.drawImageRect(
105+
image,
106+
Offset.zero &
107+
Size(image.width.toDouble(), image.height.toDouble()), // src
108+
Offset(originDx, originDy) & tileSize, // dest
109+
_paint,
110+
);
111+
}
112+
113+
continue;
111114
}
115+
116+
final subCanvasPictureRecorder = PictureRecorder();
117+
final subCanvas = Canvas(subCanvasPictureRecorder);
118+
119+
rasterOptions.paintTile!(
120+
subCanvas,
121+
tileSize,
122+
tile.coordinates,
123+
tile.data.currentPublicData,
124+
_paint,
125+
({destRect}) {
126+
if (tile.data.imageInfo?.image case final image?) {
127+
//_paint.color = _paint.color.withAlpha(255 ~/ 2);
128+
129+
subCanvas.drawImageRect(
130+
image,
131+
Offset.zero &
132+
Size(image.width.toDouble(), image.height.toDouble()), // src
133+
destRect ?? (Offset.zero & tileSize), // dest
134+
_paint,
135+
);
136+
}
137+
},
138+
);
139+
140+
canvas
141+
..save()
142+
..translate(originDx, originDy)
143+
..drawPicture(subCanvasPictureRecorder.endRecording())
144+
..restore();
112145
}
113146
}
114147

115148
@override
116149
bool shouldRepaint(covariant CustomPainter oldDelegate) {
150+
// TODO
117151
return true;
118152
}
119153
}

lib/src/layer/modern_tile_layer/tile_layers/raster/tile_layer.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import 'dart:ui';
2+
13
import 'package:flutter/widgets.dart';
24
import 'package:flutter_map/flutter_map.dart';
3-
import 'package:flutter_map/src/layer/modern_tile_layer/tile_loader/raster/tile_data.dart';
5+
import 'package:flutter_map/src/layer/modern_tile_layer/tile_loader/raster/internal_tile_data.dart';
46
import 'package:flutter_map/src/layer/tile_layer/tile_scale_calculator.dart';
57

68
part 'renderer.dart';
@@ -114,7 +116,8 @@ class _RasterTileLayerState extends State<RasterTileLayer> {
114116
Widget _renderer(
115117
BuildContext context,
116118
Object layerKey,
117-
Map<({TileCoordinates coordinates, Object layerKey}), RasterTileData>
119+
Map<({TileCoordinates coordinates, Object layerKey}),
120+
InternalRasterTileData>
118121
visibleTiles,
119122
) =>
120123
_RasterRenderer(

0 commit comments

Comments
 (0)