void StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget, const ColorPattern& aColor, const StrokeOptions& aStrokeOptions) { if (aRect.IsEmpty()) { return; } Point p1 = aRect.TopLeft(); Point p2 = aRect.BottomLeft(); SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); p1 = aRect.BottomLeft(); p2 = aRect.BottomRight(); SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); p1 = aRect.TopLeft(); p2 = aRect.TopRight(); SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); p1 = aRect.TopRight(); p2 = aRect.BottomRight(); SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); }
Rect Matrix::TransformBounds(const Rect &aRect) const { int i; Point quad[4]; Float min_x, max_x; Float min_y, max_y; quad[0] = *this * aRect.TopLeft(); quad[1] = *this * aRect.TopRight(); quad[2] = *this * aRect.BottomLeft(); quad[3] = *this * aRect.BottomRight(); min_x = max_x = quad[0].x; min_y = max_y = quad[0].y; for (i = 1; i < 4; i++) { if (quad[i].x < min_x) min_x = quad[i].x; if (quad[i].x > max_x) max_x = quad[i].x; if (quad[i].y < min_y) min_y = quad[i].y; if (quad[i].y > max_y) max_y = quad[i].y; } return Rect(min_x, min_y, max_x - min_x, max_y - min_y); }
// XXX snapToPixels is only valid when snapping for filled // rectangles and for even-width stroked rectangles. // For odd-width stroked rectangles, we need to offset x/y by // 0.5... void gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels) { Rect rec = ToRect(rect); if (snapToPixels) { gfxRect newRect(rect); if (UserToDevicePixelSnapped(newRect, true)) { gfxMatrix mat = ThebesMatrix(mTransform); if (mat.Invert()) { // We need the user space rect. rec = ToRect(mat.TransformBounds(newRect)); } else { rec = Rect(); } } } if (!mPathBuilder && !mPathIsRect) { mPathIsRect = true; mRect = rec; return; } EnsurePathBuilder(); mPathBuilder->MoveTo(rec.TopLeft()); mPathBuilder->LineTo(rec.TopRight()); mPathBuilder->LineTo(rec.BottomRight()); mPathBuilder->LineTo(rec.BottomLeft()); mPathBuilder->Close(); }
void DrawTargetD2D1::PushClipRect(const Rect &aRect) { if (!mTransform.IsRectilinear()) { // Whoops, this isn't a rectangle in device space, Direct2D will not deal // with this transform the way we want it to. // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx RefPtr<PathBuilder> pathBuilder = CreatePathBuilder(); pathBuilder->MoveTo(aRect.TopLeft()); pathBuilder->LineTo(aRect.TopRight()); pathBuilder->LineTo(aRect.BottomRight()); pathBuilder->LineTo(aRect.BottomLeft()); pathBuilder->Close(); RefPtr<Path> path = pathBuilder->Finish(); return PushClip(path); } PushedClip clip; Rect rect = mTransform.TransformBounds(aRect); IntRect intRect; clip.mIsPixelAligned = rect.ToIntRect(&intRect); // Do not store the transform, just store the device space rectangle directly. clip.mBounds = D2DRect(rect); mPushedClips.push_back(clip); mDC->SetTransform(D2D1::IdentityMatrix()); mTransformDirty = true; if (mClipsArePushed) { mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); } }
void AppendRectToPath(PathBuilder* aPathBuilder, const Rect& aRect, bool aDrawClockwise) { if (aDrawClockwise) { aPathBuilder->MoveTo(aRect.TopLeft()); aPathBuilder->LineTo(aRect.TopRight()); aPathBuilder->LineTo(aRect.BottomRight()); aPathBuilder->LineTo(aRect.BottomLeft()); } else { aPathBuilder->MoveTo(aRect.TopRight()); aPathBuilder->LineTo(aRect.TopLeft()); aPathBuilder->LineTo(aRect.BottomLeft()); aPathBuilder->LineTo(aRect.BottomRight()); } aPathBuilder->Close(); }
void TopSubMenuItem::Pull() { Rect r = GetScreenRect(); if(parentmenu && parentmenu->IsChild() && !parentmenu->submenu) parentmenu->SetupRestoreFocus(); Point p = r.BottomLeft(); if(GUI_GlobalStyle() >= GUISTYLE_XP) p += style->pullshift; SubMenuBase::Pull(this, p, Size(r.Width(), -r.Height())); if(parentmenu) parentmenu->SyncState(); }
ConvexPolygon::ConvexPolygon(const Rect& aRect) { if (!aRect.width || !aRect.height) { return; } mPoints.reserve(4); mPoints.push_back(aRect.BottomRight()); mPoints.push_back(aRect.TopRight()); mPoints.push_back(aRect.TopLeft()); mPoints.push_back(aRect.BottomLeft()); EnsureAntiClockwise(); }
Rect Matrix4x4::ProjectRectBounds(const Rect& aRect) const { Point4D points[4]; points[0] = ProjectPoint(aRect.TopLeft()); points[1] = ProjectPoint(aRect.TopRight()); points[2] = ProjectPoint(aRect.BottomRight()); points[3] = ProjectPoint(aRect.BottomLeft()); Float min_x = std::numeric_limits<Float>::max(); Float min_y = std::numeric_limits<Float>::max(); Float max_x = -std::numeric_limits<Float>::max(); Float max_y = -std::numeric_limits<Float>::max(); bool foundPoint = false; for (int i=0; i<4; i++) { // Only use points that exist above the w=0 plane if (points[i].HasPositiveWCoord()) { foundPoint = true; Point point2d = points[i].As2DPoint(); min_x = min<Float>(point2d.x, min_x); max_x = max<Float>(point2d.x, max_x); min_y = min<Float>(point2d.y, min_y); max_y = max<Float>(point2d.y, max_y); } int next = (i == 3) ? 0 : i + 1; if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) { // If the line between two points crosses the w=0 plane, then interpolate a point // as close to the w=0 plane as possible and use that instead. Point4D intercept = ComputePerspectivePlaneIntercept(points[i], points[next]); Point point2d = intercept.As2DPoint(); min_x = min<Float>(point2d.x, min_x); max_x = max<Float>(point2d.x, max_x); min_y = min<Float>(point2d.y, min_y); max_y = max<Float>(point2d.y, max_y); } } if (!foundPoint) { return Rect(0, 0, 0, 0); } return Rect(min_x, min_y, max_x - min_x, max_y - min_y); }
Rect Matrix4x4::ProjectRectBounds(const Rect& aRect, const Rect &aClip) const { // This function must never return std::numeric_limits<Float>::max() or any // other arbitrary large value in place of inifinity. This often occurs when // aRect is an inversed projection matrix or when aRect is transformed to be // partly behind and in front of the camera (w=0 plane in homogenous // coordinates) - See Bug 1035611 // Some call-sites will call RoundGfxRectToAppRect which clips both the // extents and dimensions of the rect to be bounded by nscoord_MAX. // If we return a Rect that, when converted to nscoords, has a width or height // greater than nscoord_MAX, RoundGfxRectToAppRect will clip the overflow // off both the min and max end of the rect after clipping the extents of the // rect, resulting in a translation of the rect towards the infinite end. // The bounds returned by ProjectRectBounds are expected to be clipped only on // the edges beyond the bounds of the coordinate system; otherwise, the // clipped bounding box would be smaller than the correct one and result // bugs such as incorrect culling (eg. Bug 1073056) // To address this without requiring all code to work in homogenous // coordinates or interpret infinite values correctly, a specialized // clipping function is integrated into ProjectRectBounds. // Callers should pass an aClip value that represents the extents to clip // the result to, in the same coordinate system as aRect. Point4D points[4]; points[0] = ProjectPoint(aRect.TopLeft()); points[1] = ProjectPoint(aRect.TopRight()); points[2] = ProjectPoint(aRect.BottomRight()); points[3] = ProjectPoint(aRect.BottomLeft()); Float min_x = std::numeric_limits<Float>::max(); Float min_y = std::numeric_limits<Float>::max(); Float max_x = -std::numeric_limits<Float>::max(); Float max_y = -std::numeric_limits<Float>::max(); for (int i=0; i<4; i++) { // Only use points that exist above the w=0 plane if (points[i].HasPositiveWCoord()) { Point point2d = aClip.ClampPoint(points[i].As2DPoint()); min_x = std::min<Float>(point2d.x, min_x); max_x = std::max<Float>(point2d.x, max_x); min_y = std::min<Float>(point2d.y, min_y); max_y = std::max<Float>(point2d.y, max_y); } int next = (i == 3) ? 0 : i + 1; if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) { // If the line between two points crosses the w=0 plane, then interpolate // to find the point of intersection with the w=0 plane and use that // instead. Point4D intercept = ComputePerspectivePlaneIntercept(points[i], points[next]); // Since intercept.w will always be 0 here, we interpret x,y,z as a // direction towards an infinite vanishing point. if (intercept.x < 0.0f) { min_x = aClip.x; } else if (intercept.x > 0.0f) { max_x = aClip.XMost(); } if (intercept.y < 0.0f) { min_y = aClip.y; } else if (intercept.y > 0.0f) { max_y = aClip.YMost(); } } } if (max_x < min_x || max_y < min_y) { return Rect(0, 0, 0, 0); } return Rect(min_x, min_y, max_x - min_x, max_y - min_y); }
void FormView::DrawGroupTools(Draw& w, const Rect& r) { if (!IsLayout()) return; _groupRect = r; Point p; Rect t; int v; p = r.TopLeft(); v = (r.BottomLeft().y >= GetRect().Height()) ? GetRect().Height() - (GetRect().Height() - r.TopLeft().y) / 2 - 10 : ((r.TopLeft().y < 0) ? r.BottomLeft().y / 2 - 10 : r.CenterLeft().y - 10); if (p.x >= 20) // left tool { t = Rect(Point(r.CenterLeft().x - 10, v), Size(11, 11) ); w.DrawImage(t, _toolLeft[_leftCur]); } else { t = Rect(Point(2, v), Size(11, 11) ); w.DrawImage(t, _toolLeft[_leftCur]); } v = (r.TopLeft().x < 0) ? r.TopRight().x / 2 - 5 : (r.TopRight().x > GetRect().Width() ? GetRect().Width() - (GetRect().Width() - r.TopLeft().x) / 2 - 5 : r.TopCenter().x - 5); if (p.y >= 20) { t = Rect( Point(v, r.TopCenter().y - 10), Size(11, 11) ); // top tool w.DrawImage(t, _toolTop[_topCur]); } else { t = Rect( Point(v, 2), Size(11, 11) ); // top tool w.DrawImage(t, _toolTop[_topCur]); } p = r.BottomRight(); v = (p.y >= GetRect().Height()) ? GetRect().Height() - (GetRect().Height() - r.TopLeft().y) / 2 - 10 : ((r.TopLeft().y < 0) ? r.BottomLeft().y / 2 - 110 : r.CenterLeft().y - 10); if (p.x <= GetRect().Width() - 20) { t = Rect( Point(r.CenterRight().x, v), Size(11, 11) ); // right tool w.DrawImage(t, _toolRight[_rightCur]); } else { t = Rect( Point(GetRect().Width() - 11, v), Size(11, 11) ); // right tool w.DrawImage(t, _toolRight[_rightCur]); } v = (r.TopLeft().x < 0) ? r.TopRight().x / 2 - 5 : (r.TopRight().x > GetRect().Width() ? GetRect().Width() - (GetRect().Width() - r.TopLeft().x) / 2 - 5 : r.TopCenter().x - 5); if (p.y <= GetRect().Height() - 20) { t = Rect( Point(v, r.BottomCenter().y), Size(11, 11) ); // bottom tool w.DrawImage(t, _toolBottom[_bottomCur]); } else { t = Rect( Point(v, GetRect().Height() - 11), Size(11, 11) ); // bottom tool w.DrawImage(t, _toolBottom[_bottomCur]); } }
void AppendRoundedRectToPath(PathBuilder* aPathBuilder, const Rect& aRect, const RectCornerRadii& aRadii, bool aDrawClockwise) { // For CW drawing, this looks like: // // ...******0** 1 C // **** // *** 2 // ** // * // * // 3 // * // * // // Where 0, 1, 2, 3 are the control points of the Bezier curve for // the corner, and C is the actual corner point. // // At the start of the loop, the current point is assumed to be // the point adjacent to the top left corner on the top // horizontal. Note that corner indices start at the top left and // continue clockwise, whereas in our loop i = 0 refers to the top // right corner. // // When going CCW, the control points are swapped, and the first // corner that's drawn is the top left (along with the top segment). // // There is considerable latitude in how one chooses the four // control points for a Bezier curve approximation to an ellipse. // For the overall path to be continuous and show no corner at the // endpoints of the arc, points 0 and 3 must be at the ends of the // straight segments of the rectangle; points 0, 1, and C must be // collinear; and points 3, 2, and C must also be collinear. This // leaves only two free parameters: the ratio of the line segments // 01 and 0C, and the ratio of the line segments 32 and 3C. See // the following papers for extensive discussion of how to choose // these ratios: // // Dokken, Tor, et al. "Good approximation of circles by // curvature-continuous Bezier curves." Computer-Aided // Geometric Design 7(1990) 33--41. // Goldapp, Michael. "Approximation of circular arcs by cubic // polynomials." Computer-Aided Geometric Design 8(1991) 227--238. // Maisonobe, Luc. "Drawing an elliptical arc using polylines, // quadratic, or cubic Bezier curves." // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf // // We follow the approach in section 2 of Goldapp (least-error, // Hermite-type approximation) and make both ratios equal to // // 2 2 + n - sqrt(2n + 28) // alpha = - * --------------------- // 3 n - 4 // // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ). // // This is the result of Goldapp's equation (10b) when the angle // swept out by the arc is pi/2, and the parameter "a-bar" is the // expression given immediately below equation (21). // // Using this value, the maximum radial error for a circle, as a // fraction of the radius, is on the order of 0.2 x 10^-3. // Neither Dokken nor Goldapp discusses error for a general // ellipse; Maisonobe does, but his choice of control points // follows different constraints, and Goldapp's expression for // 'alpha' gives much smaller radial error, even for very flat // ellipses, than Maisonobe's equivalent. // // For the various corners and for each axis, the sign of this // constant changes, or it might be 0 -- it's multiplied by the // appropriate multiplier from the list before using. const Float alpha = Float(0.55191497064665766025); typedef struct { Float a, b; } twoFloats; twoFloats cwCornerMults[4] = { { -1, 0 }, // cc == clockwise { 0, -1 }, { +1, 0 }, { 0, +1 } }; twoFloats ccwCornerMults[4] = { { +1, 0 }, // ccw == counter-clockwise { 0, -1 }, { -1, 0 }, { 0, +1 } }; twoFloats *cornerMults = aDrawClockwise ? cwCornerMults : ccwCornerMults; Point cornerCoords[] = { aRect.TopLeft(), aRect.TopRight(), aRect.BottomRight(), aRect.BottomLeft() }; Point pc, p0, p1, p2, p3; if (aDrawClockwise) { aPathBuilder->MoveTo(Point(aRect.X() + aRadii[RectCorner::TopLeft].width, aRect.Y())); } else { aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aRadii[RectCorner::TopRight].width, aRect.Y())); } for (int i = 0; i < 4; ++i) { // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) int c = aDrawClockwise ? ((i+1) % 4) : ((4-i) % 4); // i+2 and i+3 respectively. These are used to index into the corner // multiplier table, and were deduced by calculating out the long form // of each corner and finding a pattern in the signs and values. int i2 = (i+2) % 4; int i3 = (i+3) % 4; pc = cornerCoords[c]; if (aRadii[c].width > 0.0 && aRadii[c].height > 0.0) { p0.x = pc.x + cornerMults[i].a * aRadii[c].width; p0.y = pc.y + cornerMults[i].b * aRadii[c].height; p3.x = pc.x + cornerMults[i3].a * aRadii[c].width; p3.y = pc.y + cornerMults[i3].b * aRadii[c].height; p1.x = p0.x + alpha * cornerMults[i2].a * aRadii[c].width; p1.y = p0.y + alpha * cornerMults[i2].b * aRadii[c].height; p2.x = p3.x - alpha * cornerMults[i3].a * aRadii[c].width; p2.y = p3.y - alpha * cornerMults[i3].b * aRadii[c].height; aPathBuilder->LineTo(p0); aPathBuilder->BezierTo(p1, p2, p3); } else { aPathBuilder->LineTo(pc); } } aPathBuilder->Close(); }