Skip to content

Commit 1e90075

Browse files
authored
Strokes can have shaders too (#38)
1 parent 3dc2ce4 commit 1e90075

File tree

4 files changed

+197
-39
lines changed

4 files changed

+197
-39
lines changed

packages/vector_graphics_codec/lib/vector_graphics_codec.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,9 @@ class VectorGraphicsCodec {
317317
int strokeJoin,
318318
int blendMode,
319319
double strokeMiterLimit,
320-
double strokeWidth,
321-
) {
320+
double strokeWidth, [
321+
int? shaderId,
322+
]) {
322323
if (buffer._decodePhase.index > _CurrentSection.paints.index) {
323324
throw StateError('Paints must be encoded together.');
324325
}
@@ -333,6 +334,7 @@ class VectorGraphicsCodec {
333334
buffer._putFloat32(strokeMiterLimit);
334335
buffer._putFloat32(strokeWidth);
335336
buffer._putUint16(paintId);
337+
buffer._putUint16(shaderId ?? kMaxId);
336338
return paintId;
337339
}
338340

@@ -428,6 +430,7 @@ class VectorGraphicsCodec {
428430
final double strokeMiterLimit = buffer.getFloat32();
429431
final double strokeWidth = buffer.getFloat32();
430432
final int id = buffer.getUint16();
433+
final int shaderId = buffer.getUint16();
431434

432435
listener?.onPaintObject(
433436
color: color,
@@ -438,7 +441,7 @@ class VectorGraphicsCodec {
438441
strokeWidth: strokeWidth,
439442
paintStyle: 1, // Stroke
440443
id: id,
441-
shaderId: null,
444+
shaderId: shaderId == kMaxId ? null : shaderId,
442445
);
443446
}
444447

packages/vector_graphics_codec/test/vector_graphics_codec_test.dart

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,75 @@ void main() {
9191
]);
9292
});
9393

94+
test('Basic message encode and decode with shaded path', () {
95+
final buffer = VectorGraphicsBuffer();
96+
final TestListener listener = TestListener();
97+
final int shaderId = codec.writeLinearGradient(
98+
buffer,
99+
fromX: 0,
100+
fromY: 0,
101+
toX: 1,
102+
toY: 1,
103+
colors: Int32List.fromList([0, 1]),
104+
offsets: Float32List.fromList([0, 1]),
105+
tileMode: 1,
106+
);
107+
final int fillId = codec.writeFill(buffer, 23, 0, shaderId);
108+
final int strokeId =
109+
codec.writeStroke(buffer, 44, 1, 2, 3, 4.0, 6.0, shaderId);
110+
final int pathId = codec.writeStartPath(buffer, 0);
111+
codec.writeMoveTo(buffer, 1, 2);
112+
codec.writeLineTo(buffer, 2, 3);
113+
codec.writeClose(buffer);
114+
codec.writeFinishPath(buffer);
115+
codec.writeDrawPath(buffer, pathId, fillId);
116+
codec.writeDrawPath(buffer, pathId, strokeId);
117+
118+
codec.decode(buffer.done(), listener);
119+
120+
expect(listener.commands, [
121+
OnLinearGradient(
122+
fromX: 0,
123+
fromY: 0,
124+
toX: 1,
125+
toY: 1,
126+
colors: Int32List.fromList([0, 1]),
127+
offsets: Float32List.fromList([0, 1]),
128+
tileMode: 1,
129+
id: shaderId,
130+
),
131+
OnPaintObject(
132+
color: 23,
133+
strokeCap: null,
134+
strokeJoin: null,
135+
blendMode: 0,
136+
strokeMiterLimit: null,
137+
strokeWidth: null,
138+
paintStyle: 0,
139+
id: fillId,
140+
shaderId: shaderId,
141+
),
142+
OnPaintObject(
143+
color: 44,
144+
strokeCap: 1,
145+
strokeJoin: 2,
146+
blendMode: 3,
147+
strokeMiterLimit: 4.0,
148+
strokeWidth: 6.0,
149+
paintStyle: 1,
150+
id: strokeId,
151+
shaderId: shaderId,
152+
),
153+
OnPathStart(pathId, 0),
154+
const OnPathMoveTo(1, 2),
155+
const OnPathLineTo(2, 3),
156+
const OnPathClose(),
157+
const OnPathFinished(),
158+
OnDrawPath(pathId, fillId),
159+
OnDrawPath(pathId, strokeId),
160+
]);
161+
});
162+
94163
test('Basic message encode and decode with stroked vertex', () {
95164
final buffer = VectorGraphicsBuffer();
96165
final TestListener listener = TestListener();
@@ -568,6 +637,19 @@ class OnLinearGradient {
568637
other.tileMode == tileMode &&
569638
other.id == id;
570639
}
640+
641+
@override
642+
String toString() {
643+
return 'OnLinearGradient('
644+
'fromX: $fromX, '
645+
'toX: $toX, '
646+
'fromY: $fromY, '
647+
'toY: $toY, '
648+
'colors: Int32List.fromList($colors), '
649+
'offsets: Float32List.fromList($offsets), '
650+
'tileMode: $tileMode, '
651+
'id: $id)';
652+
}
571653
}
572654

573655
class OnRadialGradient {

packages/vector_graphics_compiler/lib/vector_graphics_compiler.dart

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,49 @@ Float64List? _encodeMatrix(AffineMatrix? matrix) {
3737
return matrix.toMatrix4();
3838
}
3939

40+
void _encodeShader(
41+
Gradient? shader,
42+
Map<Gradient, int> shaderIds,
43+
VectorGraphicsCodec codec,
44+
VectorGraphicsBuffer buffer,
45+
) {
46+
if (shader == null) {
47+
return;
48+
}
49+
int shaderId;
50+
if (shader is LinearGradient) {
51+
shaderId = codec.writeLinearGradient(
52+
buffer,
53+
fromX: shader.from.x,
54+
fromY: shader.from.y,
55+
toX: shader.to.x,
56+
toY: shader.to.y,
57+
colors: Int32List.fromList(
58+
<int>[for (Color color in shader.colors!) color.value]),
59+
offsets: Float32List.fromList(shader.offsets!),
60+
tileMode: shader.tileMode!.index,
61+
);
62+
} else if (shader is RadialGradient) {
63+
shaderId = codec.writeRadialGradient(
64+
buffer,
65+
centerX: shader.center.x,
66+
centerY: shader.center.y,
67+
radius: shader.radius,
68+
focalX: shader.focalPoint?.x,
69+
focalY: shader.focalPoint?.y,
70+
colors: Int32List.fromList(
71+
<int>[for (Color color in shader.colors!) color.value]),
72+
offsets: Float32List.fromList(shader.offsets!),
73+
tileMode: shader.tileMode!.index,
74+
transform: _encodeMatrix(shader.transform),
75+
);
76+
} else {
77+
assert(false);
78+
throw StateError('illegal shader type: $shader');
79+
}
80+
shaderIds[shader] = shaderId;
81+
}
82+
4083
/// Encode an SVG [input] string into a vector_graphics binary format.
4184
Future<Uint8List> encodeSvg(String input, String filename) async {
4285
const VectorGraphicsCodec codec = VectorGraphicsCodec();
@@ -50,42 +93,8 @@ Future<Uint8List> encodeSvg(String input, String filename) async {
5093
final Map<Gradient, int> shaderIds = <Gradient, int>{};
5194

5295
for (final Paint paint in instructions.paints) {
53-
final Gradient? shader = paint.fill?.shader;
54-
if (shader == null) {
55-
continue;
56-
}
57-
int shaderId;
58-
if (shader is LinearGradient) {
59-
shaderId = codec.writeLinearGradient(
60-
buffer,
61-
fromX: shader.from.x,
62-
fromY: shader.from.y,
63-
toX: shader.to.x,
64-
toY: shader.to.y,
65-
colors: Int32List.fromList(
66-
<int>[for (Color color in shader.colors!) color.value]),
67-
offsets: Float32List.fromList(shader.offsets!),
68-
tileMode: shader.tileMode!.index,
69-
);
70-
} else if (shader is RadialGradient) {
71-
shaderId = codec.writeRadialGradient(
72-
buffer,
73-
centerX: shader.center.x,
74-
centerY: shader.center.y,
75-
radius: shader.radius,
76-
focalX: shader.focalPoint?.x,
77-
focalY: shader.focalPoint?.y,
78-
colors: Int32List.fromList(
79-
<int>[for (Color color in shader.colors!) color.value]),
80-
offsets: Float32List.fromList(shader.offsets!),
81-
tileMode: shader.tileMode!.index,
82-
transform: _encodeMatrix(shader.transform),
83-
);
84-
} else {
85-
assert(false);
86-
throw StateError('illegal shader type: $shader');
87-
}
88-
shaderIds[shader] = shaderId;
96+
_encodeShader(paint.fill?.shader, shaderIds, codec, buffer);
97+
_encodeShader(paint.stroke?.shader, shaderIds, codec, buffer);
8998
}
9099

91100
int nextPaintId = 0;
@@ -104,6 +113,7 @@ Future<Uint8List> encodeSvg(String input, String filename) async {
104113
fillIds[nextPaintId] = fillId;
105114
}
106115
if (stroke != null) {
116+
final int? shaderId = shaderIds[stroke.shader];
107117
final int strokeId = codec.writeStroke(
108118
buffer,
109119
stroke.color?.value ?? 0,
@@ -112,6 +122,7 @@ Future<Uint8List> encodeSvg(String input, String filename) async {
112122
paint.blendMode?.index ?? 0,
113123
stroke.miterLimit ?? 4,
114124
stroke.width ?? 1,
125+
shaderId,
115126
);
116127
strokeIds[nextPaintId] = strokeId;
117128
}

packages/vector_graphics_compiler/test/end_to_end_test.dart

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,21 @@ import 'dart:typed_data';
33
import 'package:flutter/widgets.dart';
44
import 'package:flutter_test/flutter_test.dart';
55
import 'package:vector_graphics/vector_graphics.dart';
6+
import 'package:vector_graphics_codec/vector_graphics_codec.dart';
67
import 'package:vector_graphics_compiler/vector_graphics_compiler.dart';
78

89
import 'test_svg_strings.dart';
10+
import '../../vector_graphics_codec/test/vector_graphics_codec_test.dart'
11+
show
12+
TestListener,
13+
OnSize,
14+
OnLinearGradient,
15+
OnPaintObject,
16+
OnPathStart,
17+
OnPathMoveTo,
18+
OnPathLineTo,
19+
OnPathFinished,
20+
OnDrawPath;
921

1022
class TestBytesLoader extends BytesLoader {
1123
TestBytesLoader(this.data);
@@ -32,4 +44,54 @@ void main() {
3244
expect(tester.takeException(), isNull);
3345
}
3446
});
47+
48+
test('encodeSvg encodes stroke shaders', () async {
49+
const String svg = '''
50+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
51+
<defs>
52+
<linearGradient id="j" x1="69" y1="59" x2="36" y2="84" gradientUnits="userSpaceOnUse">
53+
<stop offset="0" stop-color="#ffffff" />
54+
<stop offset="1" stop-color="#000000" />
55+
</linearGradient>
56+
</defs>
57+
<g>
58+
<path d="M34 76h23" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="8" stroke="url(#j)" />
59+
</g>
60+
</svg>
61+
''';
62+
63+
final Uint8List bytes = await encodeSvg(svg, 'test');
64+
const VectorGraphicsCodec codec = VectorGraphicsCodec();
65+
final TestListener listener = TestListener();
66+
codec.decode(bytes.buffer.asByteData(), listener);
67+
expect(listener.commands, <Object>[
68+
const OnSize(120, 120),
69+
OnLinearGradient(
70+
id: 0,
71+
fromX: 69,
72+
fromY: 59,
73+
toX: 36,
74+
toY: 84,
75+
colors: Int32List.fromList(<int>[0xffffffff, 0xff000000]),
76+
offsets: Float32List.fromList(<double>[0, 1]),
77+
tileMode: 0,
78+
),
79+
const OnPaintObject(
80+
color: 0xffffffff,
81+
strokeCap: 1,
82+
strokeJoin: 1,
83+
blendMode: 0,
84+
strokeMiterLimit: 4.0,
85+
strokeWidth: 8,
86+
paintStyle: 1,
87+
id: 0,
88+
shaderId: 0,
89+
),
90+
const OnPathStart(0, 0),
91+
const OnPathMoveTo(34, 76),
92+
const OnPathLineTo(57, 76),
93+
const OnPathFinished(),
94+
const OnDrawPath(0, 0),
95+
]);
96+
});
3597
}

0 commit comments

Comments
 (0)