@@ -316,10 +316,80 @@ fn daffine2_identity() -> DAffine2 {
316316 DAffine2 :: IDENTITY
317317}
318318
319+ /// Helper module for deserializing the `Stroke::paint` field, supporting both the
320+ /// new `"paint": Fill` format and the legacy `"color": Color` format from old `.graphite` files.
321+ mod stroke_paint_migration {
322+ use super :: * ;
323+ use serde:: Deserialize ;
324+
325+ /// Legacy representation: old `.graphite` files stored a bare `Color` in a field called `"color"`.
326+ #[ derive( Deserialize ) ]
327+ struct OldStroke {
328+ #[ serde( default ) ]
329+ color : Option < Color > ,
330+ #[ serde( default ) ]
331+ paint : Option < Fill > ,
332+ #[ serde( default ) ]
333+ weight : f64 ,
334+ #[ serde( default ) ]
335+ dash_lengths : Vec < f64 > ,
336+ #[ serde( default ) ]
337+ dash_offset : f64 ,
338+ #[ serde( default , alias = "line_cap" ) ]
339+ cap : StrokeCap ,
340+ #[ serde( default , alias = "line_join" ) ]
341+ join : StrokeJoin ,
342+ #[ serde( default = "default_miter_limit" , alias = "line_join_miter_limit" ) ]
343+ join_miter_limit : f64 ,
344+ #[ serde( default ) ]
345+ align : StrokeAlign ,
346+ #[ serde( default = "super::daffine2_identity" ) ]
347+ transform : DAffine2 ,
348+ #[ serde( default ) ]
349+ non_scaling : bool ,
350+ #[ serde( default ) ]
351+ paint_order : PaintOrder ,
352+ }
353+
354+
355+ fn default_miter_limit ( ) -> f64 {
356+ 4.
357+ }
358+
359+ /// Attempt to deserialize a `Stroke` from either the new or old format.
360+ pub fn deserialize_stroke < ' de , D : serde:: Deserializer < ' de > > ( deserializer : D ) -> Result < Stroke , D :: Error > {
361+ let old = OldStroke :: deserialize ( deserializer) ?;
362+
363+ // If the new `paint` field is present, use it directly.
364+ // Otherwise, fall back to converting the legacy `color` field into `Fill::Solid`.
365+ let paint = match old. paint {
366+ Some ( paint) => paint,
367+ None => match old. color {
368+ Some ( color) => Fill :: Solid ( color) ,
369+ None => Fill :: Solid ( Color :: from_rgba8_srgb ( 0 , 0 , 0 , 255 ) ) ,
370+ } ,
371+ } ;
372+
373+ Ok ( Stroke {
374+ paint,
375+ weight : old. weight ,
376+ dash_lengths : old. dash_lengths ,
377+ dash_offset : old. dash_offset ,
378+ cap : old. cap ,
379+ join : old. join ,
380+ join_miter_limit : old. join_miter_limit ,
381+ align : old. align ,
382+ transform : old. transform ,
383+ non_scaling : old. non_scaling ,
384+ paint_order : old. paint_order ,
385+ } )
386+ }
387+
388+ }
389+
319390#[ repr( C ) ]
320391#[ cfg_attr( feature = "wasm" , derive( tsify:: Tsify ) ) ]
321- #[ derive( Debug , Clone , PartialEq , serde:: Serialize , serde:: Deserialize , DynAny ) ]
322- #[ serde( default ) ]
392+ #[ derive( Debug , Clone , PartialEq , serde:: Serialize , DynAny ) ]
323393pub struct Stroke {
324394 /// Stroke paint (solid color or gradient)
325395 pub paint : Fill ,
@@ -337,10 +407,17 @@ pub struct Stroke {
337407 pub align : StrokeAlign ,
338408 #[ serde( default = "daffine2_identity" ) ]
339409 pub transform : DAffine2 ,
340- # [ serde ( default ) ]
410+ pub non_scaling : bool ,
341411 pub paint_order : PaintOrder ,
342412}
343413
414+
415+ impl < ' de > serde:: Deserialize < ' de > for Stroke {
416+ fn deserialize < D : serde:: Deserializer < ' de > > ( deserializer : D ) -> Result < Self , D :: Error > {
417+ stroke_paint_migration:: deserialize_stroke ( deserializer)
418+ }
419+ }
420+
344421impl std:: hash:: Hash for Stroke {
345422 fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
346423 self . paint . hash ( state) ;
@@ -355,8 +432,10 @@ impl std::hash::Hash for Stroke {
355432 self . join_miter_limit . to_bits ( ) . hash ( state) ;
356433 self . align . hash ( state) ;
357434 self . transform . to_cols_array ( ) . iter ( ) . for_each ( |x| x. to_bits ( ) . hash ( state) ) ;
435+ self . non_scaling . hash ( state) ;
358436 self . paint_order . hash ( state) ;
359437 }
438+
360439}
361440
362441impl Stroke {
@@ -371,10 +450,12 @@ impl Stroke {
371450 join_miter_limit : 4. ,
372451 align : StrokeAlign :: Center ,
373452 transform : DAffine2 :: IDENTITY ,
453+ non_scaling : false ,
374454 paint_order : PaintOrder :: StrokeAbove ,
375455 }
376456 }
377457
458+
378459 pub fn lerp ( & self , other : & Self , time : f64 ) -> Self {
379460 Self {
380461 paint : self . paint . lerp ( & other. paint , time) ,
@@ -389,10 +470,12 @@ impl Stroke {
389470 time * self . transform . matrix2 + ( 1. - time) * other. transform . matrix2 ,
390471 self . transform . translation * time + other. transform . translation * ( 1. - time) ,
391472 ) ,
473+ non_scaling : if time < 0.5 { self . non_scaling } else { other. non_scaling } ,
392474 paint_order : if time < 0.5 { self . paint_order } else { other. paint_order } ,
393475 }
394476 }
395477
478+
396479 /// Get the current stroke color.
397480 pub fn color ( & self ) -> Option < Color > {
398481 match & self . paint {
@@ -515,6 +598,7 @@ impl Default for Stroke {
515598 join_miter_limit : 4. ,
516599 align : StrokeAlign :: Center ,
517600 transform : DAffine2 :: IDENTITY ,
601+ non_scaling : false ,
518602 paint_order : PaintOrder :: default ( ) ,
519603 }
520604 }
0 commit comments