Index: shape.cpp =================================================================== --- shape.cpp (revision 95129) +++ shape.cpp (working copy) @@ -319,47 +319,47 @@ void Shape::ComputeBounds () { - bounds = Rect (0,0,0,0); + Rect shape_bounds = ComputeShapeBounds (); + bounds = bounding_rect_for_transformed_rect (&absolute_xform, + IntersectBoundsWithClipPath (shape_bounds, false)); + //printf ("%f,%f,%f,%f\n", bounds.x, bounds.y, bounds.w, bounds.h); +} +Rect +Shape::ComputeShapeBounds () +{ if (IsEmpty ()) - return; + return Rect (); double w = framework_element_get_width (this); double h = framework_element_get_height (this); -#if FALSE - Stretch stretch = shape_get_stretch (this); - switch (stretch) { - case StretchUniform: - w = h = (w < h) ? w : h; - break; - case StretchUniformToFill: - w = h = (w > h) ? w : h; - break; - case StretchFill: - /* nothing needed here. the assignment of w/h above - is correct for this case. */ - break; - case StretchNone: - break; - } -#endif + if ((w == 0.0) && (h == 0.0)) + return Rect (); - if (w != 0.0 && h != 0.0) { - double t = shape_get_stroke_thickness (this) * .5; + double t = shape_get_stroke_thickness (this) * .5; - bounds = bounding_rect_for_transformed_rect (&absolute_xform, - IntersectBoundsWithClipPath (Rect (-t, -t, w + 2 * t, h + 2 * t), false)); + return Rect (-t, -t, w + 2 * t, h + 2 * t); +} - //printf ("%f,%f,%f,%f\n", bounds.x, bounds.y, bounds.w, bounds.h); - } +Rect +Shape::ComputeLargestRectangleBounds () +{ + Rect largest = ComputeLargestRectangle (); + if (largest.IsEmpty ()) + return largest; - /* standard "grow the rectangle by enough to cover our - asses because of cairo's floating point rendering" - thing */ -// no-op bounds.GrowBy(1); + return bounding_rect_for_transformed_rect (&absolute_xform, + IntersectBoundsWithClipPath (largest, false)); } +Rect +Shape::ComputeLargestRectangle () +{ + // by default the largest rectangle that fits into a shape is empty + return Rect (); +} + void Shape::GetSizeForBrush (cairo_t *cr, double *width, double *height) { @@ -765,6 +765,15 @@ // note: both moon_rounded_rectangle and moon_ellipse close the path } +Rect +Ellipse::ComputeLargestRectangle () +{ + double t = GetValue (Shape::StrokeThicknessProperty)->AsDouble (); + double x = (GetValue (FrameworkElement::WidthProperty)->AsDouble () - t) * cos (M_PI_2); + double y = (GetValue (FrameworkElement::HeightProperty)->AsDouble () - t) * sin (M_PI_2); + Rect largest = ComputeShapeBounds ().GrowBy (-x, -y).RoundIn (); +} + void Ellipse::OnPropertyChanged (DependencyProperty *prop) { @@ -1007,10 +1016,22 @@ return ((*rx != 0.0) && (*ry != 0.0)); } +Rect +Rectangle::ComputeLargestRectangle () +{ + double x = GetValue (Shape::StrokeThicknessProperty)->AsDouble (); + double y = x; + if (HasRadii ()) { + x += GetValue (Rectangle::RadiusXProperty)->AsDouble (); + y += GetValue (Rectangle::RadiusYProperty)->AsDouble (); + } + return ComputeShapeBounds ().GrowBy (-x, -y).RoundIn (); +} + double rectangle_get_radius_x (Rectangle *rectangle) { - return rectangle->GetValue (Rectangle::RadiusXProperty)->AsDouble(); + return rectangle->GetValue (Rectangle::RadiusXProperty)->AsDouble (); } void @@ -1182,39 +1203,36 @@ moon_line_to (path, line_get_x2 (this), line_get_y2 (this)); } -void -Line::ComputeBounds () +Rect +Line::ComputeShapeBounds () { + Rect shape_bounds = Rect (); + Value *vh, *vw; - if (Shape::MixedHeightWidth (&vh, &vw)) { - bounds = Rect (0.0, 0.0, 0.0, 0.0); - return; - } + if (Shape::MixedHeightWidth (&vh, &vw)) + return shape_bounds; double thickness = shape_get_stroke_thickness (this); - if (thickness <= 0.0) { - bounds = Rect (0.0, 0.0, 0.0, 0.0); - return; - } + if (thickness <= 0.0) + return shape_bounds; double x1 = line_get_x1 (this); double y1 = line_get_y1 (this); double x2 = line_get_x2 (this); double y2 = line_get_y2 (this); - calc_line_bounds (x1, x2, y1, y2, thickness, &bounds); + calc_line_bounds (x1, x2, y1, y2, thickness, &shape_bounds); origin.x = MIN (x1, x2); origin.y = MIN (y1, y2); // if Height and Width are specified (they could be both missing) // then we must clip the line those values if (vh && vw) { - bounds.w = MIN (bounds.w, vw->AsDouble ()); - bounds.h = MIN (bounds.h, vh->AsDouble ()); + shape_bounds.w = MIN (shape_bounds.w, vw->AsDouble ()); + shape_bounds.h = MIN (shape_bounds.h, vh->AsDouble ()); } - bounds = bounding_rect_for_transformed_rect (&absolute_xform, - IntersectBoundsWithClipPath (bounds, false)); + return shape_bounds; } void @@ -1450,23 +1468,21 @@ } } -void -Polygon::ComputeBounds () +Rect +Polygon::ComputeShapeBounds () { + Rect shape_bounds = Rect (); + Value *vh, *vw; - if (Shape::MixedHeightWidth (&vh, &vw)) { - bounds = Rect (0.0, 0.0, 0.0, 0.0); - return; - } + if (Shape::MixedHeightWidth (&vh, &vw)) + return shape_bounds; int i, count = 0; Point *points = polygon_get_points (this, &count); // the first point is a move to, resulting in an empty shape - if (!points || (count < 2)) { - bounds = Rect (0.0, 0.0, 0.0, 0.0); - return; - } + if (!points || (count < 2)) + return shape_bounds; double thickness = shape_get_stroke_thickness (this); if (thickness == 0.0) @@ -1483,10 +1499,10 @@ origin.y = MIN (origin.y, points[1].y); polygon_extend_line (&x0, &x1, &y0, &y1, thickness); - calc_line_bounds (x0, x1, y0, y1, thickness, &bounds); + calc_line_bounds (x0, x1, y0, y1, thickness, &shape_bounds); } else { - bounds.x = x1 = x0; - bounds.y = y1 = y0; + shape_bounds.x = x1 = x0; + shape_bounds.y = y1 = y0; // FIXME: we're too big for large thickness and/or steep angle Rect line_bounds; double x2 = points [1].x; @@ -1496,7 +1512,7 @@ origin.x = MIN (origin.x, MIN (x2, x3)); origin.y = MIN (origin.y, MIN (y2, y3)); - calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &bounds); + calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &shape_bounds); for (i = 3; i < count; i++) { x1 = x2; y1 = y2; @@ -1506,7 +1522,7 @@ y3 = points [i].y; origin.x = MIN (origin.x, x3); origin.y = MIN (origin.y, y3); - calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &bounds); + calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &shape_bounds); } // a polygon is a closed shape (unless it's a line) x1 = x2; @@ -1515,7 +1531,7 @@ y2 = y3; x3 = x0; y3 = y0; - calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &bounds); + calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &shape_bounds); x1 = points [count-1].x; y1 = points [count-1].y; @@ -1523,24 +1539,23 @@ y2 = y0; x3 = points [1].x; y3 = points [1].y; - calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &bounds); + calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &shape_bounds); } if (shape_get_stretch (this) != StretchNone) { - bounds.x -= x0; - bounds.y -= y0; + shape_bounds.x -= x0; + shape_bounds.y -= y0; } // if Height and Width are specified (they could be both missing) // then we must clip the path those values, and this also defines // *exactly* our bounds (whether we like them or not is another story) if (vh && vw) { - bounds.w = vw->AsDouble (); - bounds.h = vh->AsDouble (); + shape_bounds.w = vw->AsDouble (); + shape_bounds.h = vh->AsDouble (); } - bounds = bounding_rect_for_transformed_rect (&absolute_xform, - IntersectBoundsWithClipPath (bounds, false)); + return shape_bounds; } void @@ -1758,23 +1773,21 @@ return polyline_get_fill_rule (this); } -void -Polyline::ComputeBounds () +Rect +Polyline::ComputeShapeBounds () { + Rect shape_bounds = Rect (); + Value *vh, *vw; - if (Shape::MixedHeightWidth (&vh, &vw)) { - bounds = Rect (0.0, 0.0, 0.0, 0.0); - return; - } + if (Shape::MixedHeightWidth (&vh, &vw)) + return shape_bounds; int i, count = 0; Point *points = polyline_get_points (this, &count); // the first point is a move to, resulting in an empty shape - if (!points || (count < 2)) { - bounds = Rect (0.0, 0.0, 0.0, 0.0); - return; - } + if (!points || (count < 2)) + return shape_bounds; double thickness = shape_get_stroke_thickness (this); if (thickness == 0.0) @@ -1789,7 +1802,7 @@ double y2 = points [1].y; origin.x = MIN (origin.x, x2); origin.y = MIN (origin.y, y2); - calc_line_bounds (x1, x2, y1, y2, thickness, &bounds); + calc_line_bounds (x1, x2, y1, y2, thickness, &shape_bounds); } else { // FIXME: we're too big for large thickness and/or steep angle Rect line_bounds; @@ -1797,37 +1810,36 @@ double y2 = points [1].y; origin.x = MIN (origin.x, x2); origin.y = MIN (origin.y, y2); - calc_line_bounds (x1, x2, y1, y2, thickness, &bounds); + calc_line_bounds (x1, x2, y1, y2, thickness, &shape_bounds); for (i = 2; i < count; i++) { double x3 = points [i].x; double y3 = points [i].y; origin.x = MIN (origin.x, x3); origin.y = MIN (origin.y, y3); - calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &bounds); + calc_line_bounds_with_joins (x1, y1, x2, y2, x3, y3, thickness, &shape_bounds); x1 = x2; y1 = y2; x2 = x3; y2 = y3; } calc_line_bounds (x1, x2, y1, y2, thickness, &line_bounds); - bounds = bounds.Union (line_bounds); + shape_bounds = shape_bounds.Union (line_bounds); } if (shape_get_stretch (this) != StretchNone) { - bounds.x -= points [0].x; - bounds.y -= points [0].y; + shape_bounds.x -= points [0].x; + shape_bounds.y -= points [0].y; } // if Height and Width are specified (they could be both missing) // then we must clip the path those values, and this also defines // *exactly* our bounds (whether we like them or not is another story) if (vh && vw) { - bounds.w = vw->AsDouble (); - bounds.h = vh->AsDouble (); + shape_bounds.w = vw->AsDouble (); + shape_bounds.h = vh->AsDouble (); } - bounds = bounding_rect_for_transformed_rect (&absolute_xform, - IntersectBoundsWithClipPath (bounds, false)); + return shape_bounds; } void @@ -1997,20 +2009,20 @@ return geometry ? geometry_get_fill_rule (geometry) : Shape::GetFillRule (); } -void -Path::ComputeBounds () +Rect +Path::ComputeShapeBounds () { - bounds = Rect (0.0, 0.0, 0.0, 0.0); + Rect shape_bounds = Rect (); cairo_matrix_init_identity (&stretch_transform); Value *vh, *vw; if (Shape::MixedHeightWidth (&vh, &vw)) - return; + return shape_bounds; Geometry* geometry = path_get_data (this); if (!geometry) { SetShapeFlags (UIElement::SHAPE_EMPTY); - return; + return shape_bounds; } double w = vw ? vw->AsDouble () : 0.0; @@ -2018,30 +2030,30 @@ if ((h < 0.0) || (w < 0.0)) { SetShapeFlags (UIElement::SHAPE_EMPTY); - return; + return shape_bounds; } if (vh && (h <= 0.0) || vw && (w <= 0.0)) { SetShapeFlags (UIElement::SHAPE_EMPTY); - return; + return shape_bounds; } - bounds = geometry->ComputeBounds (this); + shape_bounds = geometry->ComputeBounds (this); - h = (h == 0.0) ? bounds.h : h; - w = (w == 0.0) ? bounds.w : w; + h = (h == 0.0) ? shape_bounds.h : h; + w = (w == 0.0) ? shape_bounds.w : w; if (h <= 0.0 || w <= 0.0) { SetShapeFlags (UIElement::SHAPE_EMPTY); - return; + return shape_bounds; } // Compute the transformation we use for stretching Stretch stretch = shape_get_stretch (this); if (stretch != StretchNone) { - double sh = h / bounds.h; - double sw = w / bounds.w; + double sh = h / shape_bounds.h; + double sw = w / shape_bounds.w; switch (stretch) { case StretchFill: break; @@ -2057,28 +2069,26 @@ } cairo_matrix_scale (&stretch_transform, sw, sh); - cairo_matrix_translate (&stretch_transform, -bounds.x, -bounds.y); + cairo_matrix_translate (&stretch_transform, -shape_bounds.x, -shape_bounds.y); // Double check our math cairo_matrix_t test = stretch_transform; if (cairo_matrix_invert (&test)) { cairo_matrix_init_identity (&stretch_transform); - g_warning ("Unable to compute stretch transform %f %f %f %f \n", sw, sh, bounds.x, bounds.y); + g_warning ("Unable to compute stretch transform %f %f %f %f \n", sw, sh, shape_bounds.x, shape_bounds.y); } } - bounds = bounding_rect_for_transformed_rect (&stretch_transform, bounds); + shape_bounds = bounding_rect_for_transformed_rect (&stretch_transform, shape_bounds); if (vh && vw) { - bounds.w = MIN (bounds.w, vw->AsDouble ()); - bounds.h = MIN (bounds.h, vh->AsDouble ()); + shape_bounds.w = MIN (shape_bounds.w, vw->AsDouble ()); + shape_bounds.h = MIN (shape_bounds.h, vh->AsDouble ()); } - bounds = bounding_rect_for_transformed_rect (&absolute_xform, - IntersectBoundsWithClipPath (bounds, false)); //FIXME - origin = Point (bounds.x, bounds.y); - + origin = Point (shape_bounds.x, shape_bounds.y); + return shape_bounds; } void Index: shape.h =================================================================== --- shape.h (revision 95129) +++ shape.h (working copy) @@ -49,6 +49,9 @@ moon_path *path; virtual void InvalidatePathCache (bool free = false); + + virtual Rect ComputeShapeBounds (); + virtual Rect ComputeLargestRectangle (); public: static DependencyProperty* FillProperty; static DependencyProperty* StretchProperty; @@ -74,9 +77,9 @@ virtual void ComputeBounds (); virtual bool InsideObject (cairo_t *cr, double x, double y); virtual Point GetTransformOrigin (); - virtual Point GetOriginPoint () { - return origin; - } + virtual Point GetOriginPoint () { return origin; } + + Rect ComputeLargestRectangleBounds (); // // new virtual methods for shapes @@ -142,6 +145,7 @@ class Ellipse : public Shape { protected: virtual bool DrawShape (cairo_t *cr, bool do_op); + virtual Rect ComputeLargestRectangle (); public: Ellipse (); virtual Type::Kind GetObjectType () { return Type::ELLIPSE; }; @@ -169,11 +173,12 @@ virtual Type::Kind GetObjectType () { return Type::RECTANGLE; }; virtual void BuildPath (); + virtual bool CanFill () { return true; } virtual void OnPropertyChanged (DependencyProperty *prop); bool GetRadius (double *rx, double *ry); - virtual bool CanFill () { return true; } + virtual Rect ComputeLargestRectangle (); }; Rectangle *rectangle_new (void); @@ -189,6 +194,7 @@ class Line : public Shape { protected: virtual bool DrawShape (cairo_t *cr, bool do_op); + virtual Rect ComputeShapeBounds (); public: static DependencyProperty* X1Property; static DependencyProperty* Y1Property; @@ -198,7 +204,6 @@ virtual Type::Kind GetObjectType () { return Type::LINE; }; virtual void BuildPath (); - virtual void ComputeBounds (); virtual bool ClipOnHeightAndWidth () { return true; } virtual void OnPropertyChanged (DependencyProperty *prop); @@ -224,6 +229,7 @@ class Polygon : public Shape { protected: virtual bool DrawShape (cairo_t *cr, bool do_op); + virtual Rect ComputeShapeBounds (); public: static DependencyProperty* FillRuleProperty; static DependencyProperty* PointsProperty; @@ -241,7 +247,6 @@ virtual bool CanFill () { return true; } virtual void GetSizeForBrush (cairo_t *cr, double *width, double *height); - virtual void ComputeBounds (); virtual bool ClipOnHeightAndWidth () { return true; } virtual void OnPropertyChanged (DependencyProperty *prop); @@ -261,6 +266,7 @@ class Polyline : public Shape { protected: virtual bool DrawShape (cairo_t *cr, bool do_op); + virtual Rect ComputeShapeBounds (); public: static DependencyProperty* FillRuleProperty; static DependencyProperty* PointsProperty; @@ -275,7 +281,6 @@ virtual bool CanFill () { return true; } virtual void GetSizeForBrush (cairo_t *cr, double *width, double *height); - virtual void ComputeBounds (); virtual bool ClipOnHeightAndWidth () { return true; } virtual FillRule GetFillRule (); @@ -298,6 +303,8 @@ protected: virtual bool SetupLine (cairo_t* cr); virtual bool DrawShape (cairo_t *cr, bool do_op); + virtual Rect ComputeShapeBounds (); + cairo_matrix_t stretch_transform; public: static DependencyProperty* DataProperty; @@ -312,7 +319,6 @@ virtual void Draw (cairo_t *cr); virtual void GetSizeForBrush (cairo_t *cr, double *width, double *height); - virtual void ComputeBounds (); virtual bool ClipOnHeightAndWidth () { return true; } virtual bool CanFill () { return true; }