@@ -557,6 +557,95 @@ 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+ final Rect tallThin = Rect .fromLTRB (
572+ min (rect.left, rect.right) - 10 ,
573+ rect.top,
574+ min (rect.left, rect.right) - 10 ,
575+ rect.bottom,
576+ );
577+ final Rect wideThin = Rect .fromLTRB (
578+ rect.left,
579+ min (rect.top, rect.bottom) - 10 ,
580+ rect.right,
581+ min (rect.top, rect.bottom) - 10 ,
582+ );
583+
584+ canvas.save ();
585+ canvas.translate (x, y);
586+
587+ paint.style = PaintingStyle .fill;
588+ canvas.drawRect (rect, paint);
589+ canvas.drawRect (tallThin, paint);
590+ canvas.drawRect (wideThin, paint);
591+
592+ canvas.save ();
593+ canvas.translate (0 , 100 );
594+ paint.style = PaintingStyle .stroke;
595+ canvas.drawRect (rect, paint);
596+ canvas.drawRect (tallThin, paint);
597+ canvas.drawRect (wideThin, paint);
598+ canvas.restore ();
599+
600+ canvas.save ();
601+ canvas.translate (100 , 0 );
602+ paint.style = PaintingStyle .fill;
603+ canvas.drawOval (rect, paint);
604+ canvas.drawOval (tallThin, paint);
605+ canvas.drawOval (wideThin, paint);
606+ canvas.restore ();
607+
608+ canvas.save ();
609+ canvas.translate (100 , 100 );
610+ paint.style = PaintingStyle .stroke;
611+ canvas.drawOval (rect, paint);
612+ canvas.drawOval (tallThin, paint);
613+ canvas.drawOval (wideThin, paint);
614+ canvas.restore ();
615+
616+ canvas.save ();
617+ canvas.translate (50 , 50 );
618+
619+ canvas.save ();
620+ canvas.clipRect (rect);
621+ canvas.drawPaint (paint);
622+ canvas.restore ();
623+
624+ canvas.save ();
625+ canvas.clipRect (tallThin);
626+ canvas.drawPaint (paint);
627+ canvas.restore ();
628+
629+ canvas.save ();
630+ canvas.clipRect (wideThin);
631+ canvas.drawPaint (paint);
632+ canvas.restore ();
633+
634+ canvas.restore ();
635+
636+ canvas.restore ();
637+ }
638+
639+ draw (const Rect .fromLTRB (10 , 10 , 40 , 40 ), 50 , 50 , const Color (0xFF2196F3 ));
640+ draw (const Rect .fromLTRB (40 , 10 , 10 , 40 ), 250 , 50 , const Color (0xFF4CAF50 ));
641+ draw (const Rect .fromLTRB (10 , 40 , 40 , 10 ), 50 , 250 , const Color (0xFF9C27B0 ));
642+ draw (const Rect .fromLTRB (40 , 40 , 10 , 10 ), 250 , 250 , const Color (0xFFFF9800 ));
643+
644+ final Picture picture = recorder.endRecording ();
645+ final Image image = await picture.toImage (450 , 450 );
646+ await comparer.addGoldenImage (image, 'render_unordered_rects.png' );
647+ });
648+
560649 Matcher closeToTransform (Float64List expected) => (dynamic v) {
561650 Expect .type <Float64List >(v);
562651 final Float64List value = v as Float64List ;
@@ -676,83 +765,82 @@ void main() async {
676765 Expect .fail ('$value is too close to $expected ' );
677766 };
678767
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 );
768+ test ('Canvas.clipRect affects canvas.getClipBounds' , () async {
769+ void testRect (Rect clipRect, bool doAA) {
770+ final PictureRecorder recorder = PictureRecorder ();
771+ final Canvas canvas = Canvas (recorder);
772+ canvas.clipRect (clipRect, doAntiAlias: doAA);
773+
774+ final Rect clipSortedBounds = Rect .fromLTRB (
775+ min (clipRect.left, clipRect.right),
776+ min (clipRect.top, clipRect.bottom),
777+ max (clipRect.left, clipRect.right),
778+ max (clipRect.top, clipRect.bottom),
779+ );
780+ Rect clipExpandedBounds;
781+ if (doAA) {
782+ clipExpandedBounds = Rect .fromLTRB (
783+ clipSortedBounds.left.floorToDouble (),
784+ clipSortedBounds.top.floorToDouble (),
785+ clipSortedBounds.right.ceilToDouble (),
786+ clipSortedBounds.bottom.ceilToDouble (),
787+ );
788+ } else {
789+ clipExpandedBounds = clipSortedBounds;
790+ }
724791
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));
792+ // Save initial return values for testing restored values
793+ final Rect initialLocalBounds = canvas.getLocalClipBounds ();
794+ final Rect initialDestinationBounds = canvas.getDestinationClipBounds ();
795+ expect (initialLocalBounds, closeToRect (clipExpandedBounds));
796+ expect (initialDestinationBounds, closeToRect (clipExpandedBounds));
797+
798+ canvas.save ();
799+ canvas.clipRect (const Rect .fromLTRB (0 , 0 , 15 , 15 ));
800+ // Both clip bounds have changed
801+ expect (canvas.getLocalClipBounds (), notCloseToRect (clipExpandedBounds));
802+ expect (canvas.getDestinationClipBounds (), notCloseToRect (clipExpandedBounds));
803+ // Previous return values have not changed
804+ expect (initialLocalBounds, closeToRect (clipExpandedBounds));
805+ expect (initialDestinationBounds, closeToRect (clipExpandedBounds));
806+ canvas.restore ();
807+
808+ // save/restore returned the values to their original values
809+ expect (canvas.getLocalClipBounds (), initialLocalBounds);
810+ expect (canvas.getDestinationClipBounds (), initialDestinationBounds);
811+
812+ canvas.save ();
813+ canvas.scale (2 , 2 );
814+ final Rect scaledExpandedBounds = Rect .fromLTRB (
815+ clipExpandedBounds.left / 2.0 ,
816+ clipExpandedBounds.top / 2.0 ,
817+ clipExpandedBounds.right / 2.0 ,
818+ clipExpandedBounds.bottom / 2.0 ,
819+ );
820+ expect (canvas.getLocalClipBounds (), closeToRect (scaledExpandedBounds));
821+ // Destination bounds are unaffected by transform
822+ expect (canvas.getDestinationClipBounds (), closeToRect (clipExpandedBounds));
823+ canvas.restore ();
824+
825+ // save/restore returned the values to their original values
826+ expect (canvas.getLocalClipBounds (), initialLocalBounds);
827+ expect (canvas.getDestinationClipBounds (), initialDestinationBounds);
828+ }
730829
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 ();
830+ testRect (const Rect .fromLTRB (10.2 , 11.3 , 20.4 , 25.7 ), false );
831+ testRect (const Rect .fromLTRB (10.2 , 11.3 , 20.4 , 25.7 ), true );
740832
741- // save/restore returned the values to their original values
742- expect (canvas. getLocalClipBounds ( ), initialLocalBounds );
743- expect (canvas. getDestinationClipBounds ( ), initialDestinationBounds );
833+ // LR swapped
834+ testRect ( const Rect . fromLTRB ( 20.4 , 11.3 , 10.2 , 25.7 ), false );
835+ testRect ( const Rect . fromLTRB ( 20.4 , 11.3 , 10.2 , 25.7 ), true );
744836
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 ();
837+ // TB swapped
838+ testRect (const Rect .fromLTRB (10.2 , 25.7 , 20.4 , 11.3 ), false );
839+ testRect (const Rect .fromLTRB (10.2 , 25.7 , 20.4 , 11.3 ), true );
752840
753- // save/restore returned the values to their original values
754- expect (canvas. getLocalClipBounds ( ), initialLocalBounds );
755- expect (canvas. getDestinationClipBounds ( ), initialDestinationBounds );
841+ // LR and TB swapped
842+ testRect ( const Rect . fromLTRB ( 20.4 , 25.7 , 10.2 , 11.3 ), false );
843+ testRect ( const Rect . fromLTRB ( 20.4 , 25.7 , 10.2 , 11.3 ), true );
756844 });
757845
758846 test ('Canvas.clipRect with matrix affects canvas.getClipBounds' , () async {
0 commit comments