@@ -16,6 +16,16 @@ import 'paragraph.dart';
16
16
import 'ruler.dart' ;
17
17
import 'text_direction.dart' ;
18
18
19
+ /// A single canvas2d context to use for all text measurements.
20
+ @visibleForTesting
21
+ final DomCanvasRenderingContext2D textContext =
22
+ // We don't use this canvas to draw anything, so let's make it as small as
23
+ // possible to save memory.
24
+ createDomCanvasElement (width: 0 , height: 0 ).context2D;
25
+
26
+ /// The last font used in the [textContext] .
27
+ String ? _lastContextFont;
28
+
19
29
/// Performs layout on a [CanvasParagraph] .
20
30
///
21
31
/// It uses a [DomCanvasElement] to measure text.
@@ -24,9 +34,6 @@ class TextLayoutService {
24
34
25
35
final CanvasParagraph paragraph;
26
36
27
- final DomCanvasRenderingContext2D context =
28
- createDomCanvasElement ().context2D;
29
-
30
37
// *** Results of layout *** //
31
38
32
39
// Look at the Paragraph class for documentation of the following properties.
@@ -53,7 +60,7 @@ class TextLayoutService {
53
60
ui.Rect get paintBounds => _paintBounds;
54
61
ui.Rect _paintBounds = ui.Rect .zero;
55
62
56
- late final Spanometer spanometer = Spanometer (paragraph, context );
63
+ late final Spanometer spanometer = Spanometer (paragraph);
57
64
58
65
late final LayoutFragmenter layoutFragmenter =
59
66
LayoutFragmenter (paragraph.plainText, paragraph.spans);
@@ -882,10 +889,9 @@ class LineBuilder {
882
889
/// it's set, the [Spanometer] updates the underlying [context] so that
883
890
/// subsequent measurements use the correct styles.
884
891
class Spanometer {
885
- Spanometer (this .paragraph, this .context );
892
+ Spanometer (this .paragraph);
886
893
887
894
final CanvasParagraph paragraph;
888
- final DomCanvasRenderingContext2D context;
889
895
890
896
static final RulerHost _rulerHost = RulerHost ();
891
897
@@ -904,21 +910,31 @@ class Spanometer {
904
910
_rulers.clear ();
905
911
}
906
912
907
- String _cssFontString = '' ;
908
-
909
913
double ? get letterSpacing => currentSpan.style.letterSpacing;
910
914
911
915
TextHeightRuler ? _currentRuler;
912
916
ParagraphSpan ? _currentSpan;
913
917
914
918
ParagraphSpan get currentSpan => _currentSpan! ;
915
919
set currentSpan (ParagraphSpan ? span) {
920
+ // Update the font string if it's different from the last applied font
921
+ // string.
922
+ //
923
+ // Also, we need to update the font string even if the span isn't changing.
924
+ // That's because `textContext` is shared across all spanometers.
925
+ if (span != null ) {
926
+ final String newCssFontString = span.style.cssFontString;
927
+ if (_lastContextFont != newCssFontString) {
928
+ _lastContextFont = newCssFontString;
929
+ textContext.font = newCssFontString;
930
+ }
931
+ }
932
+
916
933
if (span == _currentSpan) {
917
934
return ;
918
935
}
919
936
_currentSpan = span;
920
937
921
- // No need to update css font string when `span` is null.
922
938
if (span == null ) {
923
939
_currentRuler = null ;
924
940
return ;
@@ -933,13 +949,6 @@ class Spanometer {
933
949
_rulers[heightStyle] = ruler;
934
950
}
935
951
_currentRuler = ruler;
936
-
937
- // Update the font string if it's different from the previous span.
938
- final String cssFontString = span.style.cssFontString;
939
- if (_cssFontString != cssFontString) {
940
- _cssFontString = cssFontString;
941
- context.font = cssFontString;
942
- }
943
952
}
944
953
945
954
/// Whether the spanometer is ready to take measurements.
@@ -955,7 +964,7 @@ class Spanometer {
955
964
double get height => _currentRuler! .height;
956
965
957
966
double measureText (String text) {
958
- return measureSubstring (context , text, 0 , text.length);
967
+ return measureSubstring (textContext , text, 0 , text.length);
959
968
}
960
969
961
970
double measureRange (int start, int end) {
@@ -1047,7 +1056,7 @@ class Spanometer {
1047
1056
assert (end >= currentSpan.start && end <= currentSpan.end);
1048
1057
1049
1058
return measureSubstring (
1050
- context ,
1059
+ textContext ,
1051
1060
paragraph.plainText,
1052
1061
start,
1053
1062
end,
0 commit comments