Author: spouliot
Date: 2008-01-22 15:35:40 -0500 (Tue, 22 Jan 2008)
New Revision: 93570

Modified:
   trunk/moon/src/ChangeLog
   trunk/moon/src/shape.cpp
   trunk/moon/src/shape.h
Log:
2008-01-22  Sebastien Pouliot  <[EMAIL PROTECTED]>

        * shape.cpp|h: First part of DoDraw refactoring. Pass all 
        regression tests from harness (moon versus moon) but it's possible
        that some degenerated cases (without test cases) regress.



Modified: trunk/moon/src/ChangeLog
===================================================================
--- trunk/moon/src/ChangeLog    2008-01-22 20:33:53 UTC (rev 93569)
+++ trunk/moon/src/ChangeLog    2008-01-22 20:35:40 UTC (rev 93570)
@@ -1,3 +1,9 @@
+2008-01-22  Sebastien Pouliot  <[EMAIL PROTECTED]>
+
+       * shape.cpp|h: First part of DoDraw refactoring. Pass all 
+       regression tests from harness (moon versus moon) but it's possible
+       that some degenerated cases (without test cases) regress.
+
 2008-01-22  Larry Ewing  <[EMAIL PROTECTED]>
 
        * clock.cpp: initalize the smoothing to max framerate, the first

Modified: trunk/moon/src/shape.cpp
===================================================================
--- trunk/moon/src/shape.cpp    2008-01-22 20:33:53 UTC (rev 93569)
+++ trunk/moon/src/shape.cpp    2008-01-22 20:35:40 UTC (rev 93570)
@@ -1,10 +1,11 @@
 /*
  * shape.cpp: This match the classes inside System.Windows.Shapes
  *
- * Author:
+ * Authors:
  *   Miguel de Icaza ([EMAIL PROTECTED])
+ *   Sebastien Pouliot  <[EMAIL PROTECTED]>
  *
- * Copyright 2007 Novell, Inc. (http://www.novell.com)
+ * Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
  *
  * See the LICENSE file included with the distribution for details.
  * 
@@ -122,23 +123,6 @@
 }
 
 bool
-Shape::NeedsClipping ()
-{
-       if (!IsDegenerate () && (shape_get_stretch (this) == 
StretchUniformToFill))
-               return true;
-
-       // some shapes, like Line, Polyline, Polygon and Path, are clipped if 
both Height and Width properties are present
-       if (!ClipOnHeightAndWidth ())
-               return false;
-
-       Value *vh = GetValueNoDefault (FrameworkElement::HeightProperty);
-       if (!vh)
-               return false;
-
-       return (GetValueNoDefault (FrameworkElement::WidthProperty) != NULL);
-}
-
-bool
 Shape::MixedHeightWidth (Value **height, Value **width)
 {
        Value *vw = GetValueNoDefault (FrameworkElement::WidthProperty);
@@ -167,17 +151,128 @@
 //     moon_path_display (path);
 }
 
+// break up operations so we can exclude optional stuff, like:
+// * StrokeStartLineCap & StrokeEndLineCap
+// * StrokeLineJoin & StrokeMiterLimit
+// * Fill
+
+bool
+Shape::SetupLine (cairo_t* cr)
+{
+       double thickness = shape_get_stroke_thickness (this);
+       // check if something will be drawn or return 
+       // note: override this method if cairo is used to compute bounds
+       if (thickness == 0)
+               return false;
+
+       if (IsDegenerate ())
+               cairo_set_line_width (cr, 1.0);
+       else
+               cairo_set_line_width (cr, thickness);
+
+       return SetupDashes (cr, thickness);
+}
+
+bool
+Shape::SetupDashes (cairo_t *cr, double thickness)
+{
+       int count = 0;
+       double *dashes = shape_get_stroke_dash_array (this, &count);
+       if (dashes && (count > 0)) {
+               // FIXME: cairo doesn't support line cap for dashes - see 
#345894
+
+               double offset = shape_get_stroke_dash_offset (this) * thickness;
+
+               // NOTE: special case - if we continue cairo will stops drawing!
+               if ((count == 1) && (*dashes == 0.0))
+                       return false;
+
+               // multiply dashes length with thickness
+               double *dmul = new double [count];
+               for (int i=0; i < count; i++) {
+                       dmul [i] = dashes [i] * thickness;
+               }
+               cairo_set_dash (cr, dmul, count, offset);
+               delete [] dmul;
+       } else {
+               cairo_set_dash (cr, NULL, 0, 0.0);
+       }
+       return true;
+}
+
+void
+Shape::SetupLineCaps (cairo_t *cr)
+{
+       // FIXME: cairo doesn't have separate line cap for the start and end - 
see #345888
+       PenLineCap cap = shape_get_stroke_end_line_cap (this);
+       if (cap == PenLineCapFlat) {
+               cap = shape_get_stroke_start_line_cap (this);
+       }
+       cairo_set_line_cap (cr, convert_line_cap (cap));
+}
+
+void
+Shape::SetupLineJoinMiter (cairo_t *cr)
+{
+       cairo_set_line_join (cr, convert_line_join (shape_get_stroke_line_join 
(this)));
+       cairo_set_miter_limit (cr, shape_get_stroke_miter_limit (this));
+}
+
+// returns true if the path is set on the cairo, false if not
+bool
+Shape::Fill (cairo_t *cr, bool do_op)
+{
+       if (!fill)
+               return false;
+
+       Draw (cr);
+       if (do_op) {
+               fill->SetupBrush (cr, this);
+               cairo_set_fill_rule (cr, convert_fill_rule (GetFillRule ()));
+               cairo_fill_preserve (cr);
+       }
+       return true;
+}
+
+void
+Shape::Stroke (cairo_t *cr, bool do_op)
+{
+       if (do_op) {
+               stroke->SetupBrush (cr, this);
+               if (IsDegenerate ())
+                       cairo_fill_preserve (cr);
+               cairo_stroke (cr);
+       }
+}
+
+void
+Shape::Clip (cairo_t *cr)
+{
+       // some shapes, like Line, Polyline, Polygon and Path, are clipped if 
both Height and Width properties are present
+       if (ClipOnHeightAndWidth () || (!IsDegenerate () && (shape_get_stretch 
(this) == StretchUniformToFill))) {
+               Value *vh = GetValueNoDefault 
(FrameworkElement::HeightProperty);
+               if (!vh)
+                       return;
+               Value *vw = GetValueNoDefault (FrameworkElement::WidthProperty);
+               if (!vw)
+                       return;
+
+               cairo_rectangle (cr, 0, 0, vw->AsDouble (), vh->AsDouble ());
+               cairo_clip (cr);
+               cairo_new_path (cr);
+       }
+}
+
 //
 // This routine is useful for Shape derivatives: it can be used
 // to either get the bounding box from cairo, or to paint it
 //
-void 
+void
 Shape::DoDraw (cairo_t *cr, bool do_op)
 {
-       if (IsEmpty ()) {
-               cairo_new_path (cr);
-               return;
-       }
+       // quick out if, when building the path, we detected a empty shape
+       if (IsEmpty ())
+               goto cleanpath;
 
        cairo_set_matrix (cr, &absolute_xform);
 
@@ -189,107 +284,21 @@
 //             absolute_xform.x0,
 //             absolute_xform.y0);
 
-       bool drawn = false;
+       Clip (cr);
 
-       // we need to use clipping to implement StretchUniformToFill and paths 
that includes Height and Width
-       if (NeedsClipping ()) {
-               double w = framework_element_get_width (this);
-               if (w > 0.0) {
-                       double h = framework_element_get_height (this);
-                       if (h > 0.0) {
-                               cairo_rectangle (cr, 0, 0, w, h);
-                               cairo_clip (cr);
-                               cairo_new_path (cr);
-                       }
-               }
+       // if building the path detected a degenerate case
+       if (IsDegenerate ()) {
+               // FIXME: needs a DrawDegenerateShape to get more control over 
output
+               if (DrawShape (cr, do_op))
+                       return;
+       } else {
+               if (DrawShape (cr, do_op))
+                       return;
        }
 
-       // getting bounds, using cairo_stroke_extents, doesn't requires us to 
fill (consider_fill)
-       // unless there is no stroke brush assigned, which requires us to fill 
and use cairo_fill_extents
-       // also not every shapes can be filled
-       if (!IsDegenerate () && IsFilled ()) {
-               if (fill) {
-                       Draw (cr);
-                       drawn = true;
-                       if (do_op) {
-                               fill->SetupBrush (cr, this);
-                               cairo_set_fill_rule (cr, convert_fill_rule 
(GetFillRule ()));
-                               cairo_fill_preserve (cr);
-                       }
-               }
-       }
-       
-       if (stroke) {
-               double thickness = shape_get_stroke_thickness (this);
-               
-#if 0
-               // this optimization is broken wrt ComputeBoundsSlow since
-               // - thickness isn't checked there (fixed) and
-               // - the new_path clears the data for a cairo_fill_extents call 
anyway
-               //
-               // See bug #352188 for an example of what this breaks
-               if (thickness == 0) {
-                       if (drawn)
-                               cairo_new_path (cr);
-                       return;
-               }
-#endif
-
-               if (IsDegenerate ())
-                       cairo_set_line_width (cr, 1.0);
-               else
-                       cairo_set_line_width (cr, thickness);
-
-               int count = 0;
-               double *dashes = shape_get_stroke_dash_array (this, &count);
-               if (dashes && (count > 0)) {
-                       /* FIXME: cairo doesn't support line cap for dashes */
-                       double offset = shape_get_stroke_dash_offset (this) * 
thickness;
-                       // special case or cairo stops drawing
-                       if ((count == 1) && (*dashes == 0.0)) {
-                               if (drawn)
-                                       cairo_new_path (cr);
-                               return;
-                       }
-
-                       // multiply dashes length with thickness
-                       double *dmul = new double [count];
-                       for (int i=0; i < count; i++) {
-                               dmul [i] = dashes [i] * thickness;
-                       }
-                       cairo_set_dash (cr, dmul, count, offset);
-                       delete [] dmul;
-               } else {
-                       cairo_set_dash (cr, NULL, 0, 0.0);
-               }
-
-               if (NeedsLineJoin ()) {
-                       cairo_set_line_join (cr, convert_line_join 
(shape_get_stroke_line_join (this)));
-                       cairo_set_miter_limit (cr, shape_get_stroke_miter_limit 
(this));
-               }
-
-               if (NeedsLineCaps ()) {
-                       /* FIXME: cairo doesn't have separate line cap for the 
start and end */
-                       PenLineCap cap = shape_get_stroke_end_line_cap (this);
-                       if (cap == PenLineCapFlat) {
-                               cap = shape_get_stroke_start_line_cap (this);
-                       }
-                       cairo_set_line_cap (cr, convert_line_cap (cap));
-               }
-
-               if (!drawn)
-                       Draw (cr);
-               if (do_op) {
-                       stroke->SetupBrush (cr, this);
-                       if (IsDegenerate ())
-                               cairo_fill_preserve (cr);
-                       cairo_stroke (cr);
-               }
-       }
-       else {
-               if (drawn && do_op)
-                       cairo_new_path (cr);
-       }
+cleanpath:
+       if (do_op)
+               cairo_new_path (cr);
 }
 
 void
@@ -643,6 +652,27 @@
        SetValue (Shape::StretchProperty, Value (StretchFill));
 }
 
+// The Ellipse shape can be drawn while ignoring properties:
+// * Shape::StrokeStartLineCap
+// * Shape::StrokeEndLineCap
+// * Shape::StrokeLineJoin
+// * Shape::StrokeMiterLimit
+bool
+Ellipse::DrawShape (cairo_t *cr, bool do_op)
+{
+       bool drawn = Fill (cr, do_op);
+
+       if (!stroke)
+               return drawn;
+       if (!SetupLine (cr))
+               return drawn;
+
+       if (!drawn)
+               Draw (cr);
+       Stroke (cr, do_op);
+       return true; 
+}
+
 void
 Ellipse::BuildPath ()
 {
@@ -757,6 +787,32 @@
        SetValue (Shape::StretchProperty, Value (StretchFill));
 }
 
+// The Rectangle shape can be drawn while ignoring properties:
+// * Shape::StrokeStartLineCap
+// * Shape::StrokeEndLineCap
+// * Shape::StrokeLineJoin     [for rounded-corner rectangles only]
+// * Shape::StrokeMiterLimit   [for rounded-corner rectangles only]
+bool
+Rectangle::DrawShape (cairo_t *cr, bool do_op)
+{
+       bool drawn = Fill (cr, do_op);
+
+       if (!stroke)
+               return drawn;
+
+       if (!SetupLine (cr))
+               return drawn;
+
+       // FIXME: is it worth checking for round-corners ?
+       SetupLineJoinMiter (cr);
+
+       // Draw if the path wasn't drawn by the Fill call
+       if (!drawn)
+               Draw (cr);
+       Stroke (cr, do_op);
+       return true; 
+}
+
 /*
  * rendering notes:
  * - a Width="0" or a Height="0" can be rendered differently from not 
specifying Width or Height
@@ -989,6 +1045,26 @@
 DependencyProperty* Line::X2Property;
 DependencyProperty* Line::Y2Property;
 
+// The Line shape can be drawn while ignoring properties:
+// * Shape::StrokeLineJoin
+// * Shape::StrokeMiterLimit
+// * Shape::Fill
+bool
+Line::DrawShape (cairo_t *cr, bool do_op)
+{
+       // no need to clear path since none has been drawn to cairo
+       if (!stroke)
+               return false; 
+
+       if (!SetupLine (cr))
+               return false;
+       SetupLineCaps (cr);
+
+       Draw (cr);
+       Stroke (cr, do_op);
+       return true; 
+}
+
 static void
 calc_line_bounds (double x1, double x2, double y1, double y2, double 
thickness, Rect* bounds)
 {
@@ -1154,6 +1230,26 @@
        return polygon_get_fill_rule (this);
 }
 
+// The Polygon shape can be drawn while ignoring properties:
+// * Shape::StrokeStartLineCap
+// * Shape::StrokeEndLineCap
+bool
+Polygon::DrawShape (cairo_t *cr, bool do_op)
+{
+       bool drawn = Fill (cr, do_op);
+
+       if (!stroke)
+               return drawn; 
+
+       if (!SetupLine (cr))
+               return drawn;
+       SetupLineJoinMiter (cr);
+
+       Draw (cr);
+       Stroke (cr, do_op);
+       return true;
+}
+
 // special case when a polygon has a single line in it (it's drawn longer than 
it should)
 // e.g. <Polygon Fill="#000000" Stroke="#FF00FF" StrokeThickness="8" 
Points="260,80 300,40" />
 static void
@@ -1505,6 +1601,63 @@
 DependencyProperty* Polyline::FillRuleProperty;
 DependencyProperty* Polyline::PointsProperty;
 
+// The Polyline shape can be drawn while ignoring NO properties
+bool
+Polyline::DrawShape (cairo_t *cr, bool do_op)
+{
+       bool drawn = Fill (cr, do_op);
+
+       if (!stroke)
+               return drawn; 
+
+       if (!SetupLine (cr))
+               return drawn;
+       SetupLineJoinMiter (cr);
+
+       // here we hack around #345888 where Cairo doesn't support different 
start and end linecaps
+       PenLineCap start = shape_get_stroke_start_line_cap (this);
+       PenLineCap end = shape_get_stroke_end_line_cap (this);
+       if (do_op && (start != end)){
+               // the previous fill, if needed, has preserved the path
+               if (drawn)
+                       cairo_new_path (cr);
+
+               // since Draw may not have been called (e.g. no Fill) we must 
ensure the path was built
+               if (!drawn || !path || (path->cairo.num_data == 0))
+                       BuildPath ();
+
+               cairo_path_data_t *data = path->cairo.data;
+               int length = path->cairo.num_data;
+               // we need to treat a single line scenario differently (like 
Line::DrawShape)
+               if (length <= MOON_PATH_MOVE_TO_LENGTH + 
MOON_PATH_LINE_TO_LENGTH) {
+                       // note: this means either no, one or two points
+// TODO (right now a straight line is drawn (without caps)
+               } else {
+                       // draw line #1 with start cap
+                       cairo_set_line_cap (cr, convert_line_cap (start));
+                       cairo_move_to (cr, data[1].point.x, data[1].point.y);
+                       cairo_line_to (cr, data[3].point.x, data[3].point.y);
+                       Stroke (cr, do_op);
+
+                       // draw last line with end cap
+                       cairo_set_line_cap (cr, convert_line_cap (end));
+                       cairo_move_to (cr, data[length - 3].point.x, 
data[length - 3].point.y);
+                       cairo_line_to (cr, data[length - 1].point.x, 
data[length - 1].point.y);
+                       Stroke (cr, do_op);
+               }
+
+               // now all lines (including first and last) will be drawn with 
no caps
+               // note: important for IsInside and other stuff depending on 
Cairo context
+               cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+       } else {
+               cairo_set_line_cap (cr, convert_line_cap (start));
+       }
+
+       Draw (cr);
+       Stroke (cr, do_op);
+       return true;
+}
+
 FillRule
 Polyline::GetFillRule ()
 {
@@ -1702,6 +1855,39 @@
 
 DependencyProperty* Path::DataProperty;
 
+bool
+Path::SetupLine (cairo_t* cr)
+{
+       // we cannot use the thickness==0 optimization (like Shape::SetupLine 
provides)
+       // since we'll be using cairo to compute the path's bounds later
+       // see bug #352188 for an example of what this breaks
+       double thickness = IsDegenerate () ? 1.0 : shape_get_stroke_thickness 
(this);
+       cairo_set_line_width (cr, thickness);
+       return SetupDashes (cr, thickness);
+}
+
+// The Polygon shape can be drawn while ignoring properties:
+// * none 
+// FIXME: actually it depends on the geometry, another level of optimization 
awaits ;-)
+// e.g. close geometries don't need to setup line caps, 
+//     line join/miter don't applies to curve, like EllipseGeometry
+bool
+Path::DrawShape (cairo_t *cr, bool do_op)
+{
+       bool drawn = Shape::Fill (cr, do_op);
+       if (stroke) {
+               if (!SetupLine (cr))
+                       return drawn;   // return if we have a path in the 
cairo_t
+               SetupLineCaps (cr);
+               SetupLineJoinMiter (cr);
+
+               if (!drawn)
+                       Draw (cr);
+               Stroke (cr, do_op);
+       }
+       return true;
+}
+
 FillRule
 Path::GetFillRule ()
 {

Modified: trunk/moon/src/shape.h
===================================================================
--- trunk/moon/src/shape.h      2008-01-22 20:33:53 UTC (rev 93569)
+++ trunk/moon/src/shape.h      2008-01-22 20:35:40 UTC (rev 93570)
@@ -36,6 +36,16 @@
        Brush *stroke, *fill;
        void DoDraw (cairo_t *cr, bool do_op);
 
+       void SetupLineCaps (cairo_t *cr);
+       void SetupLineJoinMiter (cairo_t *cr);
+       virtual bool SetupLine (cairo_t* cr);
+       bool SetupDashes (cairo_t *cr, double thickness);
+       bool Fill (cairo_t *cr, bool do_op);
+       void Stroke (cairo_t *cr, bool do_op);
+       void Clip (cairo_t *cr);
+       virtual bool DrawShape (cairo_t *cr, bool do_op) = 0;
+//     virtual bool DrawDegenerateShape (cairo_t *cr, bool do_op) = 0;
+
        moon_path *path;
        virtual void InvalidatePathCache (bool free = false);
  public: 
@@ -68,19 +78,10 @@
        // new virtual methods for shapes
        //
        
-       virtual bool IsFilled () { return fill; }
        virtual bool IsStroked () { return stroke; }
        virtual bool CanFill () { return false; }
        virtual FillRule GetFillRule () { return FillRuleNonzero; }
 
-       // Stroke[Start|End]LineCap properties are ignored for some shapes
-       // e.g. Ellipse, Rectangle, Polygon and (closed) Path
-       virtual bool NeedsLineCaps () { return true; }
-
-       // StrokeLineJoin & StrokeMiterLimit properties are ignored for some 
shapes
-       // e.g. Ellipse, Rectangle (with rounded corners), Line
-       virtual bool NeedsLineJoin () { return true; }
-
        //
        // Draw: draws the Shape in the cairo context (affine transforms are 
set before this
        // is called). 
@@ -132,14 +133,14 @@
 // Ellipse
 //
 class Ellipse : public Shape {
+ protected:
+       virtual bool DrawShape (cairo_t *cr, bool do_op);
  public:
        Ellipse ();
        virtual Type::Kind GetObjectType () { return Type::ELLIPSE; };
 
        virtual void BuildPath ();
        virtual bool CanFill () { return true; }
-       virtual bool NeedsLineCaps () { return false; }
-       virtual bool NeedsLineJoin () { return false; }
 
        virtual void OnPropertyChanged (DependencyProperty *prop);
 };
@@ -151,6 +152,8 @@
 // Rectangle class 
 // 
 class Rectangle : public Shape {
+ protected:
+       virtual bool DrawShape (cairo_t *cr, bool do_op);
  public:
        static DependencyProperty* RadiusXProperty;
        static DependencyProperty* RadiusYProperty;
@@ -164,9 +167,6 @@
 
        bool GetRadius (double *rx, double *ry);
        virtual bool CanFill () { return true; }
-       virtual bool NeedsLineCaps () { return false; }
-       // technically we could override NeedsLineJoin to check for round 
corners
-       // but that would be just as expensive as setting the unneeded values
 };
 
 Rectangle *rectangle_new          (void);
@@ -180,6 +180,8 @@
 // Line class 
 // 
 class Line : public Shape {
+ protected:
+       virtual bool DrawShape (cairo_t *cr, bool do_op);
  public:
        static DependencyProperty* X1Property;
        static DependencyProperty* Y1Property;
@@ -196,9 +198,6 @@
 
        // Line has no center to compute, it's always 0,0 because it provides 
it's own start and end
        // virtual Point GetTransformOrigin ();
-
-       virtual bool IsFilled () { return false; }
-       virtual bool NeedsLineJoin () { return false; }
 };
 
 Line *line_new  (void);
@@ -216,6 +215,8 @@
 // Polygon
 //
 class Polygon : public Shape {
+ protected:
+       virtual bool DrawShape (cairo_t *cr, bool do_op);
  public:
        static DependencyProperty* FillRuleProperty;
        static DependencyProperty* PointsProperty;
@@ -231,7 +232,6 @@
        virtual FillRule GetFillRule ();
 
        virtual bool CanFill () { return true; }
-       virtual bool NeedsLineCaps () { return false; }
 
        virtual void GetSizeForBrush (cairo_t *cr, double *width, double 
*height);
        virtual void ComputeBounds ();
@@ -252,6 +252,8 @@
 // Polyline
 //
 class Polyline : public Shape {
+ protected:
+       virtual bool DrawShape (cairo_t *cr, bool do_op);
  public:
        static DependencyProperty* FillRuleProperty;
        static DependencyProperty* PointsProperty;
@@ -286,6 +288,9 @@
 // Path
 //
 class Path : public Shape {
+ protected:
+       virtual bool SetupLine (cairo_t* cr);
+       virtual bool DrawShape (cairo_t *cr, bool do_op);
  public:
        static DependencyProperty* DataProperty;
 

_______________________________________________
Mono-patches maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches

Reply via email to