Skip to content

Commit cdb483f

Browse files
add support for linear and radial gradient (#11)
1 parent 55debbd commit cdb483f

File tree

8 files changed

+550
-38
lines changed

8 files changed

+550
-38
lines changed

packages/vector_graphics/lib/src/listener.dart

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class FlutterVectorGraphicsListener extends VectorGraphicsCodecListener {
1919
final ui.Canvas _canvas;
2020
final List<ui.Paint> _paints = <ui.Paint>[];
2121
final List<ui.Path> _paths = <ui.Path>[];
22+
final List<ui.Shader> _shaders = <ui.Shader>[];
2223
ui.Path? _currentPath;
2324
bool _done = false;
2425

@@ -65,14 +66,18 @@ class FlutterVectorGraphicsListener extends VectorGraphicsCodecListener {
6566
required double? strokeWidth,
6667
required int paintStyle,
6768
required int id,
69+
required int? shaderId,
6870
}) {
6971
assert(_paints.length == id, 'Expect ID to be ${_paints.length}');
7072
final ui.Paint paint = ui.Paint()..color = ui.Color(color);
71-
7273
if (blendMode != 0) {
7374
paint.blendMode = ui.BlendMode.values[blendMode];
7475
}
7576

77+
if (shaderId != null) {
78+
paint.shader = _shaders[shaderId];
79+
}
80+
7681
if (paintStyle == 1) {
7782
paint.style = ui.PaintingStyle.stroke;
7883
if (strokeCap != null && strokeCap != 0) {
@@ -137,4 +142,64 @@ class FlutterVectorGraphicsListener extends VectorGraphicsCodecListener {
137142
void onSaveLayer(int paintId) {
138143
_canvas.saveLayer(null, _paints[paintId]);
139144
}
145+
146+
@override
147+
void onLinearGradient(
148+
double fromX,
149+
double fromY,
150+
double toX,
151+
double toY,
152+
Int32List colors,
153+
Float32List? offsets,
154+
int tileMode,
155+
int id,
156+
) {
157+
assert(_shaders.length == id);
158+
159+
final ui.Offset from = ui.Offset(fromX, fromY);
160+
final ui.Offset to = ui.Offset(toX, toY);
161+
final List<ui.Color> colorValues = <ui.Color>[
162+
for (int i = 0; i < colors.length; i++) ui.Color(colors[i])
163+
];
164+
final ui.Gradient gradient = ui.Gradient.linear(
165+
from,
166+
to,
167+
colorValues,
168+
offsets,
169+
ui.TileMode.values[tileMode],
170+
);
171+
_shaders.add(gradient);
172+
}
173+
174+
@override
175+
void onRadialGradient(
176+
double centerX,
177+
double centerY,
178+
double radius,
179+
double? focalX,
180+
double? focalY,
181+
Int32List colors,
182+
Float32List? offsets,
183+
int tileMode,
184+
int id,
185+
) {
186+
assert(_shaders.length == id);
187+
188+
final ui.Offset center = ui.Offset(centerX, centerY);
189+
final ui.Offset? focal = focalX == null ? null : ui.Offset(focalX, focalY!);
190+
final List<ui.Color> colorValues = <ui.Color>[
191+
for (int i = 0; i < colors.length; i++) ui.Color(colors[i])
192+
];
193+
final ui.Gradient gradient = ui.Gradient.radial(
194+
center,
195+
radius,
196+
colorValues,
197+
offsets,
198+
ui.TileMode.values[tileMode],
199+
null,
200+
focal,
201+
radius,
202+
);
203+
_shaders.add(gradient);
204+
}
140205
}

packages/vector_graphics_codec/lib/vector_graphics_codec.dart

Lines changed: 195 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class VectorGraphicsCodec {
2020
static const int _finishPathTag = 36;
2121
static const int _saveLayer = 37;
2222
static const int _restore = 38;
23+
static const int _linearGradientTag = 39;
24+
static const int _radialGradientTag = 40;
2325

2426
static const int _version = 1;
2527
static const int _magicNumber = 0x00882d62;
@@ -50,6 +52,12 @@ class VectorGraphicsCodec {
5052
while (buffer.hasRemaining) {
5153
final int type = buffer.getUint8();
5254
switch (type) {
55+
case _linearGradientTag:
56+
_readLinearGradient(buffer, listener);
57+
continue;
58+
case _radialGradientTag:
59+
_readRadialGradient(buffer, listener);
60+
continue;
5361
case _fillPaintTag:
5462
_readFillPaint(buffer, listener);
5563
continue;
@@ -149,8 +157,9 @@ class VectorGraphicsCodec {
149157
int writeFill(
150158
VectorGraphicsBuffer buffer,
151159
int color,
152-
int blendMode,
153-
) {
160+
int blendMode, [
161+
int? shaderId,
162+
]) {
154163
if (buffer._decodePhase.index > _CurrentSection.paints.index) {
155164
throw StateError('Paints must be encoded together.');
156165
}
@@ -160,9 +169,90 @@ class VectorGraphicsCodec {
160169
buffer._putUint32(color);
161170
buffer._putUint8(blendMode);
162171
buffer._putInt32(paintId);
172+
buffer._putInt32(shaderId ?? -1);
163173
return paintId;
164174
}
165175

176+
/// Write a linear gradient into the current buffer.
177+
int writeLinearGradient(
178+
VectorGraphicsBuffer buffer, {
179+
required double fromX,
180+
required double fromY,
181+
required double toX,
182+
required double toY,
183+
required Int32List colors,
184+
required Float32List? offsets,
185+
required int tileMode,
186+
}) {
187+
if (buffer._decodePhase.index > _CurrentSection.shaders.index) {
188+
throw StateError('Shaders must be encoded together.');
189+
}
190+
buffer._decodePhase = _CurrentSection.shaders;
191+
final int shaderId = buffer._nextShaderId++;
192+
buffer._putUint8(_linearGradientTag);
193+
buffer._putUint32(shaderId);
194+
buffer._putFloat64(fromX);
195+
buffer._putFloat64(fromY);
196+
buffer._putFloat64(toX);
197+
buffer._putFloat64(toY);
198+
buffer._putInt32(colors.length);
199+
buffer._putInt32List(colors);
200+
if (offsets == null) {
201+
buffer._putInt32(0);
202+
} else {
203+
buffer._putInt32(offsets.length);
204+
buffer._putFloat32List(offsets);
205+
}
206+
buffer._putUint8(tileMode);
207+
return shaderId;
208+
}
209+
210+
/// Write a radial gradient into the current buffer.
211+
///
212+
/// [focalX] and [focalY] must be either both `null` or both `non-null`.
213+
int writeRadialGradient(
214+
VectorGraphicsBuffer buffer, {
215+
required double centerX,
216+
required double centerY,
217+
required double radius,
218+
required double? focalX,
219+
required double? focalY,
220+
required Int32List colors,
221+
required Float32List? offsets,
222+
required int tileMode,
223+
}) {
224+
assert((focalX == null && focalY == null) ||
225+
(focalX != null && focalY != null));
226+
if (buffer._decodePhase.index > _CurrentSection.shaders.index) {
227+
throw StateError('Shaders must be encoded together.');
228+
}
229+
buffer._decodePhase = _CurrentSection.shaders;
230+
final int shaderId = buffer._nextShaderId++;
231+
buffer._putUint8(_radialGradientTag);
232+
buffer._putInt32(shaderId);
233+
buffer._putFloat64(centerX);
234+
buffer._putFloat64(centerY);
235+
buffer._putFloat64(radius);
236+
237+
if (focalX != null) {
238+
buffer._putUint8(1);
239+
buffer._putFloat64(focalX);
240+
buffer._putFloat64(focalY!);
241+
} else {
242+
buffer._putUint8(0);
243+
}
244+
buffer._putInt32(colors.length);
245+
buffer._putInt32List(colors);
246+
if (offsets != null) {
247+
buffer._putInt32(offsets.length);
248+
buffer._putFloat32List(offsets);
249+
} else {
250+
buffer._putInt32(0);
251+
}
252+
buffer._putUint8(tileMode);
253+
return shaderId;
254+
}
255+
166256
/// Encode a paint object in the current buffer, returning the identifier
167257
/// assigned to it.
168258
///
@@ -197,11 +287,71 @@ class VectorGraphicsCodec {
197287
return paintId;
198288
}
199289

290+
void _readLinearGradient(
291+
_ReadBuffer buffer,
292+
VectorGraphicsCodecListener? listener,
293+
) {
294+
final int id = buffer.getInt32();
295+
final double fromX = buffer.getFloat64();
296+
final double fromY = buffer.getFloat64();
297+
final double toX = buffer.getFloat64();
298+
final double toY = buffer.getFloat64();
299+
final int colorLength = buffer.getInt32();
300+
final Int32List colors = buffer.getInt32List(colorLength);
301+
final int offsetLength = buffer.getInt32();
302+
final Float32List offsets = buffer.getFloat32List(offsetLength);
303+
final int tileMode = buffer.getUint8();
304+
listener?.onLinearGradient(
305+
fromX,
306+
fromY,
307+
toX,
308+
toY,
309+
colors,
310+
offsets,
311+
tileMode,
312+
id,
313+
);
314+
}
315+
316+
void _readRadialGradient(
317+
_ReadBuffer buffer,
318+
VectorGraphicsCodecListener? listener,
319+
) {
320+
final int id = buffer.getInt32();
321+
final double centerX = buffer.getFloat64();
322+
final double centerY = buffer.getFloat64();
323+
final double radius = buffer.getFloat64();
324+
final int hasFocal = buffer.getUint8();
325+
double? focalX;
326+
double? focalY;
327+
if (hasFocal == 1) {
328+
focalX = buffer.getFloat64();
329+
focalY = buffer.getFloat64();
330+
}
331+
final int colorsLength = buffer.getInt32();
332+
final Int32List colors = buffer.getInt32List(colorsLength);
333+
final int offsetsLength = buffer.getInt32();
334+
final Float32List offsets = buffer.getFloat32List(offsetsLength);
335+
final int tileMode = buffer.getUint8();
336+
listener?.onRadialGradient(
337+
centerX,
338+
centerY,
339+
radius,
340+
focalX,
341+
focalY,
342+
colors,
343+
offsets,
344+
tileMode,
345+
id,
346+
);
347+
}
348+
200349
void _readFillPaint(
201350
_ReadBuffer buffer, VectorGraphicsCodecListener? listener) {
202351
final int color = buffer.getUint32();
203352
final int blendMode = buffer.getUint8();
204353
final int id = buffer.getInt32();
354+
final int shaderId = buffer.getInt32();
205355

206356
listener?.onPaintObject(
207357
color: color,
@@ -212,6 +362,7 @@ class VectorGraphicsCodec {
212362
strokeWidth: null,
213363
paintStyle: 0, // Fill
214364
id: id,
365+
shaderId: shaderId == -1 ? null : shaderId,
215366
);
216367
}
217368

@@ -234,6 +385,7 @@ class VectorGraphicsCodec {
234385
strokeWidth: strokeWidth,
235386
paintStyle: 1, // Stroke
236387
id: id,
388+
shaderId: null,
237389
);
238390
}
239391

@@ -405,6 +557,7 @@ abstract class VectorGraphicsCodecListener {
405557
required double? strokeWidth,
406558
required int paintStyle,
407559
required int id,
560+
required int? shaderId,
408561
});
409562

410563
/// A path object is being created, with the given [id] and [fillType].
@@ -453,9 +606,37 @@ abstract class VectorGraphicsCodecListener {
453606

454607
/// Restore the save stack.
455608
void onRestoreLayer();
609+
610+
/// A radial gradient shader has been parsed.
611+
///
612+
/// [focalX] and [focalY] are either both `null` or `non-null`.
613+
void onRadialGradient(
614+
double centerX,
615+
double centerY,
616+
double radius,
617+
double? focalX,
618+
double? focalY,
619+
Int32List colors,
620+
Float32List? offsets,
621+
int tileMode,
622+
int id,
623+
);
624+
625+
/// A linear gradient shader has been parsed.
626+
void onLinearGradient(
627+
double fromX,
628+
double fromY,
629+
double toX,
630+
double toY,
631+
Int32List colors,
632+
Float32List? offsets,
633+
int tileMode,
634+
int id,
635+
);
456636
}
457637

458638
enum _CurrentSection {
639+
shaders,
459640
paints,
460641
paths,
461642
commands,
@@ -492,6 +673,9 @@ class VectorGraphicsBuffer {
492673
/// The next path id to be used.
493674
int _nextPathId = 0;
494675

676+
/// The next shader id to be used.
677+
int _nextShaderId = 0;
678+
495679
/// The current id of the path being built, or `-1` if there is no
496680
/// active path.
497681
int _currentPathId = -1;
@@ -500,7 +684,7 @@ class VectorGraphicsBuffer {
500684
///
501685
/// Objects must be written in the correct order, the same as the
502686
/// enum order.
503-
_CurrentSection _decodePhase = _CurrentSection.paints;
687+
_CurrentSection _decodePhase = _CurrentSection.shaders;
504688

505689
/// Write a Uint8 into the buffer.
506690
void _putUint8(int byte) {
@@ -522,6 +706,14 @@ class VectorGraphicsBuffer {
522706
_buffer.addAll(_eightBytesAsList.take(4));
523707
}
524708

709+
/// Write an Int32List into the buffer.
710+
void _putInt32List(Int32List list) {
711+
assert(!_isDone);
712+
_alignTo(4);
713+
_buffer
714+
.addAll(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length));
715+
}
716+
525717
/// Write an Float64 into the buffer.
526718
void _putFloat64(double value, {Endian? endian}) {
527719
assert(!_isDone);

0 commit comments

Comments
 (0)