@@ -557,6 +557,61 @@ void main() async {
557557 expect (tabToTofuComparison, isFalse);
558558 });
559559
560+ test ('drawRect, drawOval, and clipRect render with unsorted rectangles' , () async {
561+ final PictureRecorder recorder = PictureRecorder ();
562+ final Canvas canvas = Canvas (recorder);
563+
564+ canvas.drawColor (const Color (0xFFE0E0E0 ), BlendMode .src);
565+
566+ void draw (Rect rect, double x, double y, Color color) {
567+ final Paint paint = Paint ()
568+ ..color = color
569+ ..strokeWidth = 5.0 ;
570+
571+ canvas.save ();
572+ canvas.translate (x, y);
573+
574+ paint.style = PaintingStyle .fill;
575+ canvas.drawRect (rect, paint);
576+
577+ canvas.save ();
578+ canvas.translate (0 , 100 );
579+ paint.style = PaintingStyle .stroke;
580+ canvas.drawRect (rect, paint);
581+ canvas.restore ();
582+
583+
584+ canvas.save ();
585+ canvas.translate (100 , 0 );
586+ paint.style = PaintingStyle .fill;
587+ canvas.drawOval (rect, paint);
588+ canvas.restore ();
589+
590+ canvas.save ();
591+ canvas.translate (100 , 100 );
592+ paint.style = PaintingStyle .stroke;
593+ canvas.drawOval (rect, paint);
594+ canvas.restore ();
595+
596+ canvas.save ();
597+ canvas.translate (50 , 50 );
598+ canvas.clipRect (rect);
599+ canvas.drawPaint (paint);
600+ canvas.restore ();
601+
602+ canvas.restore ();
603+ }
604+
605+ draw (const Rect .fromLTRB (10 , 10 , 40 , 40 ), 50 , 50 , const Color (0xFF2196F3 ));
606+ draw (const Rect .fromLTRB (40 , 10 , 10 , 40 ), 250 , 50 , const Color (0xFF4CAF50 ));
607+ draw (const Rect .fromLTRB (10 , 40 , 40 , 10 ), 50 , 250 , const Color (0xFF9C27B0 ));
608+ draw (const Rect .fromLTRB (40 , 40 , 10 , 10 ), 250 , 250 , const Color (0xFFFF9800 ));
609+
610+ final Picture picture = recorder.endRecording ();
611+ final Image image = await picture.toImage (450 , 450 );
612+ await comparer.addGoldenImage (image, 'render_unordered_rects.png' );
613+ });
614+
560615 Matcher closeToTransform (Float64List expected) => (dynamic v) {
561616 Expect .type <Float64List >(v);
562617 final Float64List value = v as Float64List ;
@@ -676,83 +731,82 @@ void main() async {
676731 Expect .fail ('$value is too close to $expected ' );
677732 };
678733
679- test ('Canvas.clipRect(doAA=true) affects canvas.getClipBounds' , () async {
680- final PictureRecorder recorder = PictureRecorder ();
681- final Canvas canvas = Canvas (recorder);
682- const Rect clipBounds = Rect .fromLTRB (10.2 , 11.3 , 20.4 , 25.7 );
683- const Rect clipExpandedBounds = Rect .fromLTRB (10 , 11 , 21 , 26 );
684- canvas.clipRect (clipBounds);
685-
686- // Save initial return values for testing restored values
687- final Rect initialLocalBounds = canvas.getLocalClipBounds ();
688- final Rect initialDestinationBounds = canvas.getDestinationClipBounds ();
689- expect (initialLocalBounds, closeToRect (clipExpandedBounds));
690- expect (initialDestinationBounds, closeToRect (clipExpandedBounds));
691-
692- canvas.save ();
693- canvas.clipRect (const Rect .fromLTRB (0 , 0 , 15 , 15 ));
694- // Both clip bounds have changed
695- expect (canvas.getLocalClipBounds (), notCloseToRect (clipExpandedBounds));
696- expect (canvas.getDestinationClipBounds (), notCloseToRect (clipExpandedBounds));
697- // Previous return values have not changed
698- expect (initialLocalBounds, closeToRect (clipExpandedBounds));
699- expect (initialDestinationBounds, closeToRect (clipExpandedBounds));
700- canvas.restore ();
701-
702- // save/restore returned the values to their original values
703- expect (canvas.getLocalClipBounds (), initialLocalBounds);
704- expect (canvas.getDestinationClipBounds (), initialDestinationBounds);
705-
706- canvas.save ();
707- canvas.scale (2 , 2 );
708- const Rect scaledExpandedBounds = Rect .fromLTRB (5 , 5.5 , 10.5 , 13 );
709- expect (canvas.getLocalClipBounds (), closeToRect (scaledExpandedBounds));
710- // Destination bounds are unaffected by transform
711- expect (canvas.getDestinationClipBounds (), closeToRect (clipExpandedBounds));
712- canvas.restore ();
713-
714- // save/restore returned the values to their original values
715- expect (canvas.getLocalClipBounds (), initialLocalBounds);
716- expect (canvas.getDestinationClipBounds (), initialDestinationBounds);
717- });
718-
719- test ('Canvas.clipRect(doAA=false) affects canvas.getClipBounds' , () async {
720- final PictureRecorder recorder = PictureRecorder ();
721- final Canvas canvas = Canvas (recorder);
722- const Rect clipBounds = Rect .fromLTRB (10.2 , 11.3 , 20.4 , 25.7 );
723- canvas.clipRect (clipBounds, doAntiAlias: false );
734+ test ('Canvas.clipRect affects canvas.getClipBounds' , () async {
735+ void testRect (Rect clipRect, bool doAA) {
736+ final PictureRecorder recorder = PictureRecorder ();
737+ final Canvas canvas = Canvas (recorder);
738+ canvas.clipRect (clipRect, doAntiAlias: doAA);
739+
740+ final Rect clipSortedBounds = Rect .fromLTRB (
741+ min (clipRect.left, clipRect.right),
742+ min (clipRect.top, clipRect.bottom),
743+ max (clipRect.left, clipRect.right),
744+ max (clipRect.top, clipRect.bottom),
745+ );
746+ Rect clipExpandedBounds;
747+ if (doAA) {
748+ clipExpandedBounds = Rect .fromLTRB (
749+ clipSortedBounds.left.floorToDouble (),
750+ clipSortedBounds.top.floorToDouble (),
751+ clipSortedBounds.right.ceilToDouble (),
752+ clipSortedBounds.bottom.ceilToDouble (),
753+ );
754+ } else {
755+ clipExpandedBounds = clipSortedBounds;
756+ }
724757
725- // Save initial return values for testing restored values
726- final Rect initialLocalBounds = canvas.getLocalClipBounds ();
727- final Rect initialDestinationBounds = canvas.getDestinationClipBounds ();
728- expect (initialLocalBounds, closeToRect (clipBounds));
729- expect (initialDestinationBounds, closeToRect (clipBounds));
758+ // Save initial return values for testing restored values
759+ final Rect initialLocalBounds = canvas.getLocalClipBounds ();
760+ final Rect initialDestinationBounds = canvas.getDestinationClipBounds ();
761+ expect (initialLocalBounds, closeToRect (clipExpandedBounds));
762+ expect (initialDestinationBounds, closeToRect (clipExpandedBounds));
763+
764+ canvas.save ();
765+ canvas.clipRect (const Rect .fromLTRB (0 , 0 , 15 , 15 ));
766+ // Both clip bounds have changed
767+ expect (canvas.getLocalClipBounds (), notCloseToRect (clipExpandedBounds));
768+ expect (canvas.getDestinationClipBounds (), notCloseToRect (clipExpandedBounds));
769+ // Previous return values have not changed
770+ expect (initialLocalBounds, closeToRect (clipExpandedBounds));
771+ expect (initialDestinationBounds, closeToRect (clipExpandedBounds));
772+ canvas.restore ();
773+
774+ // save/restore returned the values to their original values
775+ expect (canvas.getLocalClipBounds (), initialLocalBounds);
776+ expect (canvas.getDestinationClipBounds (), initialDestinationBounds);
777+
778+ canvas.save ();
779+ canvas.scale (2 , 2 );
780+ final Rect scaledExpandedBounds = Rect .fromLTRB (
781+ clipExpandedBounds.left / 2.0 ,
782+ clipExpandedBounds.top / 2.0 ,
783+ clipExpandedBounds.right / 2.0 ,
784+ clipExpandedBounds.bottom / 2.0 ,
785+ );
786+ expect (canvas.getLocalClipBounds (), closeToRect (scaledExpandedBounds));
787+ // Destination bounds are unaffected by transform
788+ expect (canvas.getDestinationClipBounds (), closeToRect (clipExpandedBounds));
789+ canvas.restore ();
790+
791+ // save/restore returned the values to their original values
792+ expect (canvas.getLocalClipBounds (), initialLocalBounds);
793+ expect (canvas.getDestinationClipBounds (), initialDestinationBounds);
794+ }
730795
731- canvas.save ();
732- canvas.clipRect (const Rect .fromLTRB (0 , 0 , 15 , 15 ));
733- // Both clip bounds have changed
734- expect (canvas.getLocalClipBounds (), notCloseToRect (clipBounds));
735- expect (canvas.getDestinationClipBounds (), notCloseToRect (clipBounds));
736- // Previous return values have not changed
737- expect (initialLocalBounds, closeToRect (clipBounds));
738- expect (initialDestinationBounds, closeToRect (clipBounds));
739- canvas.restore ();
796+ testRect (const Rect .fromLTRB (10.2 , 11.3 , 20.4 , 25.7 ), false );
797+ testRect (const Rect .fromLTRB (10.2 , 11.3 , 20.4 , 25.7 ), true );
740798
741- // save/restore returned the values to their original values
742- expect (canvas. getLocalClipBounds ( ), initialLocalBounds );
743- expect (canvas. getDestinationClipBounds ( ), initialDestinationBounds );
799+ // LR swapped
800+ testRect ( const Rect . fromLTRB ( 20.4 , 11.3 , 10.2 , 25.7 ), false );
801+ testRect ( const Rect . fromLTRB ( 20.4 , 11.3 , 10.2 , 25.7 ), true );
744802
745- canvas.save ();
746- canvas.scale (2 , 2 );
747- const Rect scaledClipBounds = Rect .fromLTRB (5.1 , 5.65 , 10.2 , 12.85 );
748- expect (canvas.getLocalClipBounds (), closeToRect (scaledClipBounds));
749- // Destination bounds are unaffected by transform
750- expect (canvas.getDestinationClipBounds (), closeToRect (clipBounds));
751- canvas.restore ();
803+ // TB swapped
804+ testRect (const Rect .fromLTRB (10.2 , 25.7 , 20.4 , 11.3 ), false );
805+ testRect (const Rect .fromLTRB (10.2 , 25.7 , 20.4 , 11.3 ), true );
752806
753- // save/restore returned the values to their original values
754- expect (canvas. getLocalClipBounds ( ), initialLocalBounds );
755- expect (canvas. getDestinationClipBounds ( ), initialDestinationBounds );
807+ // LR and TB swapped
808+ testRect ( const Rect . fromLTRB ( 20.4 , 25.7 , 10.2 , 11.3 ), false );
809+ testRect ( const Rect . fromLTRB ( 20.4 , 25.7 , 10.2 , 11.3 ), true );
756810 });
757811
758812 test ('Canvas.clipRect with matrix affects canvas.getClipBounds' , () async {
0 commit comments