1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Oracle Corporation code.
17 * The Initial Developer of the Original Code is Oracle Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
22 * Stuart Parmenter <pavlov@pavlov.net>
23 * Vladimir Vukicevic <vladimir@pobox.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 #define _USE_MATH_DEFINES
45 #define M_PI 3.14159265358979323846
51 #include "gfxContext.h"
54 #include "gfxMatrix.h"
55 #include "gfxASurface.h"
56 #include "gfxPattern.h"
57 #include "gfxPlatform.h"
60 gfxContext::gfxContext(gfxASurface
*surface
) :
63 mCairo
= cairo_create(surface
->CairoSurface());
64 mFlags
= surface
->GetDefaultContextFlags();
66 gfxContext::~gfxContext()
68 cairo_destroy(mCairo
);
72 gfxContext::OriginalSurface()
77 already_AddRefed
<gfxASurface
>
78 gfxContext::CurrentSurface(gfxFloat
*dx
, gfxFloat
*dy
)
80 cairo_surface_t
*s
= cairo_get_group_target(mCairo
);
81 if (s
== mSurface
->CairoSurface()) {
83 cairo_surface_get_device_offset(s
, dx
, dy
);
84 gfxASurface
*ret
= mSurface
;
90 cairo_surface_get_device_offset(s
, dx
, dy
);
91 return gfxASurface::Wrap(s
);
101 gfxContext::Restore()
103 cairo_restore(mCairo
);
108 gfxContext::NewPath()
110 cairo_new_path(mCairo
);
114 gfxContext::ClosePath()
116 cairo_close_path(mCairo
);
119 already_AddRefed
<gfxPath
> gfxContext::CopyPath() const
121 nsRefPtr
<gfxPath
> path
= new gfxPath(cairo_copy_path(mCairo
));
122 return path
.forget();
125 void gfxContext::AppendPath(gfxPath
* path
)
127 if (path
->mPath
->status
== CAIRO_STATUS_SUCCESS
&& path
->mPath
->num_data
!= 0)
128 cairo_append_path(mCairo
, path
->mPath
);
132 gfxContext::CurrentPoint() const
135 cairo_get_current_point(mCairo
, &x
, &y
);
136 return gfxPoint(x
, y
);
142 cairo_stroke_preserve(mCairo
);
148 cairo_fill_preserve(mCairo
);
152 gfxContext::MoveTo(const gfxPoint
& pt
)
154 cairo_move_to(mCairo
, pt
.x
, pt
.y
);
158 gfxContext::NewSubPath()
160 cairo_new_sub_path(mCairo
);
164 gfxContext::LineTo(const gfxPoint
& pt
)
166 cairo_line_to(mCairo
, pt
.x
, pt
.y
);
170 gfxContext::CurveTo(const gfxPoint
& pt1
, const gfxPoint
& pt2
, const gfxPoint
& pt3
)
172 cairo_curve_to(mCairo
, pt1
.x
, pt1
.y
, pt2
.x
, pt2
.y
, pt3
.x
, pt3
.y
);
176 gfxContext::QuadraticCurveTo(const gfxPoint
& pt1
, const gfxPoint
& pt2
)
179 cairo_get_current_point(mCairo
, &cx
, &cy
);
180 cairo_curve_to(mCairo
,
181 (cx
+ pt1
.x
* 2.0) / 3.0,
182 (cy
+ pt1
.y
* 2.0) / 3.0,
183 (pt1
.x
* 2.0 + pt2
.x
) / 3.0,
184 (pt1
.y
* 2.0 + pt2
.y
) / 3.0,
190 gfxContext::Arc(const gfxPoint
& center
, gfxFloat radius
,
191 gfxFloat angle1
, gfxFloat angle2
)
193 cairo_arc(mCairo
, center
.x
, center
.y
, radius
, angle1
, angle2
);
197 gfxContext::NegativeArc(const gfxPoint
& center
, gfxFloat radius
,
198 gfxFloat angle1
, gfxFloat angle2
)
200 cairo_arc_negative(mCairo
, center
.x
, center
.y
, radius
, angle1
, angle2
);
204 gfxContext::Line(const gfxPoint
& start
, const gfxPoint
& end
)
210 // XXX snapToPixels is only valid when snapping for filled
211 // rectangles and for even-width stroked rectangles.
212 // For odd-width stroked rectangles, we need to offset x/y by
215 gfxContext::Rectangle(const gfxRect
& rect
, PRBool snapToPixels
)
218 gfxRect
snappedRect(rect
);
220 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
221 if (UserToDevicePixelSnapped(snappedRect
, PR_TRUE
))
223 if (UserToDevicePixelSnapped(snappedRect
))
227 cairo_get_matrix(mCairo
, &mat
);
228 cairo_identity_matrix(mCairo
);
229 Rectangle(snappedRect
);
230 cairo_set_matrix(mCairo
, &mat
);
236 cairo_rectangle(mCairo
, rect
.pos
.x
, rect
.pos
.y
, rect
.size
.width
, rect
.size
.height
);
240 gfxContext::Ellipse(const gfxPoint
& center
, const gfxSize
& dimensions
)
242 gfxSize halfDim
= dimensions
/ 2.0;
243 gfxRect
r(center
- halfDim
, dimensions
);
244 gfxCornerSizes
c(halfDim
, halfDim
, halfDim
, halfDim
);
246 RoundedRectangle (r
, c
);
250 gfxContext::Polygon(const gfxPoint
*points
, PRUint32 numPoints
)
255 cairo_move_to(mCairo
, points
[0].x
, points
[0].y
);
256 for (PRUint32 i
= 1; i
< numPoints
; ++i
) {
257 cairo_line_to(mCairo
, points
[i
].x
, points
[i
].y
);
262 gfxContext::DrawSurface(gfxASurface
*surface
, const gfxSize
& size
)
265 cairo_set_source_surface(mCairo
, surface
->CairoSurface(), 0, 0);
266 cairo_new_path(mCairo
);
269 Rectangle(gfxRect(gfxPoint(0.0, 0.0), size
), PR_TRUE
);
272 cairo_restore(mCairo
);
277 gfxContext::Translate(const gfxPoint
& pt
)
279 cairo_translate(mCairo
, pt
.x
, pt
.y
);
283 gfxContext::Scale(gfxFloat x
, gfxFloat y
)
285 cairo_scale(mCairo
, x
, y
);
289 gfxContext::Rotate(gfxFloat angle
)
291 cairo_rotate(mCairo
, angle
);
295 gfxContext::Multiply(const gfxMatrix
& matrix
)
297 const cairo_matrix_t
& mat
= reinterpret_cast<const cairo_matrix_t
&>(matrix
);
298 cairo_transform(mCairo
, &mat
);
302 gfxContext::SetMatrix(const gfxMatrix
& matrix
)
304 const cairo_matrix_t
& mat
= reinterpret_cast<const cairo_matrix_t
&>(matrix
);
305 cairo_set_matrix(mCairo
, &mat
);
309 gfxContext::IdentityMatrix()
311 cairo_identity_matrix(mCairo
);
315 gfxContext::CurrentMatrix() const
318 cairo_get_matrix(mCairo
, &mat
);
319 return gfxMatrix(*reinterpret_cast<gfxMatrix
*>(&mat
));
323 gfxContext::DeviceToUser(const gfxPoint
& point
) const
325 gfxPoint ret
= point
;
326 cairo_device_to_user(mCairo
, &ret
.x
, &ret
.y
);
331 gfxContext::DeviceToUser(const gfxSize
& size
) const
334 cairo_device_to_user_distance(mCairo
, &ret
.width
, &ret
.height
);
339 gfxContext::DeviceToUser(const gfxRect
& rect
) const
342 cairo_device_to_user(mCairo
, &ret
.pos
.x
, &ret
.pos
.y
);
343 cairo_device_to_user_distance(mCairo
, &ret
.size
.width
, &ret
.size
.height
);
348 gfxContext::UserToDevice(const gfxPoint
& point
) const
350 gfxPoint ret
= point
;
351 cairo_user_to_device(mCairo
, &ret
.x
, &ret
.y
);
356 gfxContext::UserToDevice(const gfxSize
& size
) const
359 cairo_user_to_device_distance(mCairo
, &ret
.width
, &ret
.height
);
364 gfxContext::UserToDevice(const gfxRect
& rect
) const
366 double xmin
, ymin
, xmax
, ymax
;
369 xmax
= rect
.pos
.x
+ rect
.size
.width
;
370 ymax
= rect
.pos
.y
+ rect
.size
.height
;
373 x
[0] = xmin
; y
[0] = ymax
;
374 x
[1] = xmax
; y
[1] = ymax
;
375 x
[2] = xmax
; y
[2] = ymin
;
377 cairo_user_to_device(mCairo
, &xmin
, &ymin
);
380 for (int i
= 0; i
< 3; i
++) {
381 cairo_user_to_device(mCairo
, &x
[i
], &y
[i
]);
382 xmin
= PR_MIN(xmin
, x
[i
]);
383 xmax
= PR_MAX(xmax
, x
[i
]);
384 ymin
= PR_MIN(ymin
, y
[i
]);
385 ymax
= PR_MAX(ymax
, y
[i
]);
388 return gfxRect(xmin
, ymin
, xmax
- xmin
, ymax
- ymin
);
392 gfxContext::UserToDevicePixelSnapped(gfxRect
& rect
, PRBool ignoreScale
) const
394 if (GetFlags() & FLAG_DISABLE_SNAPPING
)
397 // if we're not at 1.0 scale, don't snap, unless we're
398 // ignoring the scale. If we're not -just- a scale,
401 cairo_get_matrix(mCairo
, &mat
);
402 if ((!ignoreScale
&& (mat
.xx
!= 1.0 || mat
.yy
!= 1.0)) ||
403 (mat
.xy
!= 0.0 || mat
.yx
!= 0.0))
406 gfxPoint p1
= UserToDevice(rect
.pos
);
407 gfxPoint p2
= UserToDevice(rect
.pos
+ rect
.size
);
409 gfxPoint p3
= UserToDevice(rect
.pos
+ gfxSize(rect
.size
.width
, 0.0));
410 gfxPoint p4
= UserToDevice(rect
.pos
+ gfxSize(0.0, rect
.size
.height
));
412 // rectangle is no longer axis-aligned after transforming, so we can't snap
422 gfxPoint pd
= p2
- p1
;
425 rect
.size
= gfxSize(pd
.x
, pd
.y
);
431 gfxContext::UserToDevicePixelSnapped(gfxPoint
& pt
, PRBool ignoreScale
) const
433 if (GetFlags() & FLAG_DISABLE_SNAPPING
)
436 // if we're not at 1.0 scale, don't snap, unless we're
437 // ignoring the scale. If we're not -just- a scale,
440 cairo_get_matrix(mCairo
, &mat
);
441 if ((!ignoreScale
&& (mat
.xx
!= 1.0 || mat
.yy
!= 1.0)) ||
442 (mat
.xy
!= 0.0 || mat
.yx
!= 0.0))
445 pt
= UserToDevice(pt
);
451 gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect
& rect
,
456 // Bob attempts to pixel-snap the rectangle, and returns true if
457 // the snapping succeeds. If it does, we need to set up an
458 // identity matrix, because the rectangle given back is in device
461 // We then have to call a translate to dr.pos afterwards, to make
462 // sure the image lines up in the right place with our pixel
463 // snapped rectangle.
465 // If snapping wasn't successful, we just translate to where the
466 // pattern would normally start (in app coordinates) and do the
469 gfxMatrix mat
= CurrentMatrix();
470 if (UserToDevicePixelSnapped(r
)) {
475 r
.pos
.x
= r
.pos
.y
= 0;
483 gfxContext::SetAntialiasMode(AntialiasMode mode
)
485 if (mode
== MODE_ALIASED
) {
486 cairo_set_antialias(mCairo
, CAIRO_ANTIALIAS_NONE
);
487 } else if (mode
== MODE_COVERAGE
) {
488 cairo_set_antialias(mCairo
, CAIRO_ANTIALIAS_DEFAULT
);
492 gfxContext::AntialiasMode
493 gfxContext::CurrentAntialiasMode() const
495 cairo_antialias_t aa
= cairo_get_antialias(mCairo
);
496 if (aa
== CAIRO_ANTIALIAS_NONE
)
498 return MODE_COVERAGE
;
502 gfxContext::SetDash(gfxLineType ltype
)
504 static double dash
[] = {5.0, 5.0};
505 static double dot
[] = {1.0, 1.0};
509 SetDash(dash
, 2, 0.0);
512 SetDash(dot
, 2, 0.0);
516 SetDash(nsnull
, 0, 0.0);
522 gfxContext::SetDash(gfxFloat
*dashes
, int ndash
, gfxFloat offset
)
524 cairo_set_dash(mCairo
, dashes
, ndash
, offset
);
526 //void getDash() const;
529 gfxContext::SetLineWidth(gfxFloat width
)
531 cairo_set_line_width(mCairo
, width
);
535 gfxContext::CurrentLineWidth() const
537 return cairo_get_line_width(mCairo
);
541 gfxContext::SetOperator(GraphicsOperator op
)
543 if (mFlags
& FLAG_SIMPLIFY_OPERATORS
) {
544 if (op
!= OPERATOR_SOURCE
&&
545 op
!= OPERATOR_CLEAR
&&
550 cairo_set_operator(mCairo
, (cairo_operator_t
)op
);
553 gfxContext::GraphicsOperator
554 gfxContext::CurrentOperator() const
556 return (GraphicsOperator
)cairo_get_operator(mCairo
);
560 gfxContext::SetLineCap(GraphicsLineCap cap
)
562 cairo_set_line_cap(mCairo
, (cairo_line_cap_t
)cap
);
565 gfxContext::GraphicsLineCap
566 gfxContext::CurrentLineCap() const
568 return (GraphicsLineCap
)cairo_get_line_cap(mCairo
);
572 gfxContext::SetLineJoin(GraphicsLineJoin join
)
574 cairo_set_line_join(mCairo
, (cairo_line_join_t
)join
);
577 gfxContext::GraphicsLineJoin
578 gfxContext::CurrentLineJoin() const
580 return (GraphicsLineJoin
)cairo_get_line_join(mCairo
);
584 gfxContext::SetMiterLimit(gfxFloat limit
)
586 cairo_set_miter_limit(mCairo
, limit
);
590 gfxContext::CurrentMiterLimit() const
592 return cairo_get_miter_limit(mCairo
);
596 gfxContext::SetFillRule(FillRule rule
)
598 cairo_set_fill_rule(mCairo
, (cairo_fill_rule_t
)rule
);
602 gfxContext::CurrentFillRule() const
604 return (FillRule
)cairo_get_fill_rule(mCairo
);
609 gfxContext::Clip(const gfxRect
& rect
)
611 cairo_new_path(mCairo
);
612 cairo_rectangle(mCairo
, rect
.pos
.x
, rect
.pos
.y
, rect
.size
.width
, rect
.size
.height
);
619 cairo_clip_preserve(mCairo
);
623 gfxContext::ResetClip()
625 cairo_reset_clip(mCairo
);
629 gfxContext::UpdateSurfaceClip()
632 Rectangle(gfxRect(0,0,0,0));
637 gfxContext::GetClipExtents()
639 double xmin
, ymin
, xmax
, ymax
;
640 cairo_clip_extents(mCairo
, &xmin
, &ymin
, &xmax
, &ymax
);
641 return gfxRect(xmin
, ymin
, xmax
- xmin
, ymax
- ymin
);
647 gfxContext::SetColor(const gfxRGBA
& c
)
649 if (gfxPlatform::GetCMSMode() == eCMSMode_All
) {
652 gfxPlatform::TransformPixel(c
, cms
, gfxPlatform::GetCMSRGBTransform());
654 // Use the original alpha to avoid unnecessary float->byte->float
656 cairo_set_source_rgba(mCairo
, cms
.r
, cms
.g
, cms
.b
, c
.a
);
659 cairo_set_source_rgba(mCairo
, c
.r
, c
.g
, c
.b
, c
.a
);
663 gfxContext::SetDeviceColor(const gfxRGBA
& c
)
665 cairo_set_source_rgba(mCairo
, c
.r
, c
.g
, c
.b
, c
.a
);
669 gfxContext::GetDeviceColor(gfxRGBA
& c
)
671 return cairo_pattern_get_rgba(cairo_get_source(mCairo
),
675 &c
.a
) == CAIRO_STATUS_SUCCESS
;
679 gfxContext::SetSource(gfxASurface
*surface
, const gfxPoint
& offset
)
681 cairo_set_source_surface(mCairo
, surface
->CairoSurface(), offset
.x
, offset
.y
);
685 gfxContext::SetPattern(gfxPattern
*pattern
)
687 cairo_set_source(mCairo
, pattern
->CairoPattern());
690 already_AddRefed
<gfxPattern
>
691 gfxContext::GetPattern()
693 cairo_pattern_t
*pat
= cairo_get_source(mCairo
);
694 NS_ASSERTION(pat
, "I was told this couldn't be null");
696 gfxPattern
*wrapper
= nsnull
;
698 wrapper
= new gfxPattern(pat
);
700 wrapper
= new gfxPattern(gfxRGBA(0,0,0,0));
702 NS_IF_ADDREF(wrapper
);
710 gfxContext::Mask(gfxPattern
*pattern
)
712 cairo_mask(mCairo
, pattern
->CairoPattern());
716 gfxContext::Mask(gfxASurface
*surface
, const gfxPoint
& offset
)
718 cairo_mask_surface(mCairo
, surface
->CairoSurface(), offset
.x
, offset
.y
);
722 gfxContext::Paint(gfxFloat alpha
)
724 cairo_paint_with_alpha(mCairo
, alpha
);
730 gfxContext::PushGroup(gfxASurface::gfxContentType content
)
732 cairo_push_group_with_content(mCairo
, (cairo_content_t
) content
);
735 already_AddRefed
<gfxPattern
>
736 gfxContext::PopGroup()
738 cairo_pattern_t
*pat
= cairo_pop_group(mCairo
);
739 gfxPattern
*wrapper
= new gfxPattern(pat
);
740 cairo_pattern_destroy(pat
);
741 NS_IF_ADDREF(wrapper
);
746 gfxContext::PopGroupToSource()
748 cairo_pop_group_to_source(mCairo
);
752 gfxContext::PointInFill(const gfxPoint
& pt
)
754 return cairo_in_fill(mCairo
, pt
.x
, pt
.y
);
758 gfxContext::PointInStroke(const gfxPoint
& pt
)
760 return cairo_in_stroke(mCairo
, pt
.x
, pt
.y
);
764 gfxContext::GetUserPathExtent()
766 double xmin
, ymin
, xmax
, ymax
;
767 cairo_path_extents(mCairo
, &xmin
, &ymin
, &xmax
, &ymax
);
768 return gfxRect(xmin
, ymin
, xmax
- xmin
, ymax
- ymin
);
772 gfxContext::GetUserFillExtent()
774 double xmin
, ymin
, xmax
, ymax
;
775 cairo_fill_extents(mCairo
, &xmin
, &ymin
, &xmax
, &ymax
);
776 return gfxRect(xmin
, ymin
, xmax
- xmin
, ymax
- ymin
);
780 gfxContext::GetUserStrokeExtent()
782 double xmin
, ymin
, xmax
, ymax
;
783 cairo_stroke_extents(mCairo
, &xmin
, &ymin
, &xmax
, &ymax
);
784 return gfxRect(xmin
, ymin
, xmax
- xmin
, ymax
- ymin
);
787 already_AddRefed
<gfxFlattenedPath
>
788 gfxContext::GetFlattenedPath()
790 gfxFlattenedPath
*path
=
791 new gfxFlattenedPath(cairo_copy_path_flat(mCairo
));
797 gfxContext::HasError()
799 return cairo_status(mCairo
) != CAIRO_STATUS_SUCCESS
;
803 gfxContext::RoundedRectangle(const gfxRect
& rect
,
804 const gfxCornerSizes
& corners
,
805 PRBool draw_clockwise
)
808 // For CW drawing, this looks like:
820 // Where 0, 1, 2, 3 are the control points of the Bezier curve for
821 // the corner, and C is the actual corner point.
823 // At the start of the loop, the current point is assumed to be
824 // the point adjacent to the top left corner on the top
825 // horizontal. Note that corner indices start at the top left and
826 // continue clockwise, whereas in our loop i = 0 refers to the top
829 // When going CCW, the control points are swapped, and the first
830 // corner that's drawn is the top left (along with the top segment).
832 // There is considerable latitude in how one chooses the four
833 // control points for a Bezier curve approximation to an ellipse.
834 // For the overall path to be continuous and show no corner at the
835 // endpoints of the arc, points 0 and 3 must be at the ends of the
836 // straight segments of the rectangle; points 0, 1, and C must be
837 // collinear; and points 3, 2, and C must also be collinear. This
838 // leaves only two free parameters: the ratio of the line segments
839 // 01 and 0C, and the ratio of the line segments 32 and 3C. See
840 // the following papers for extensive discussion of how to choose
843 // Dokken, Tor, et al. "Good approximation of circles by
844 // curvature-continuous Bezier curves." Computer-Aided
845 // Geometric Design 7(1990) 33--41.
846 // Goldapp, Michael. "Approximation of circular arcs by cubic
847 // polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
848 // Maisonobe, Luc. "Drawing an elliptical arc using polylines,
849 // quadratic, or cubic Bezier curves."
850 // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
852 // We follow the approach in section 2 of Goldapp (least-error,
853 // Hermite-type approximation) and make both ratios equal to
855 // 2 2 + n - sqrt(2n + 28)
856 // alpha = - * ---------------------
859 // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
861 // This is the result of Goldapp's equation (10b) when the angle
862 // swept out by the arc is pi/2, and the parameter "a-bar" is the
863 // expression given immediately below equation (21).
865 // Using this value, the maximum radial error for a circle, as a
866 // fraction of the radius, is on the order of 0.2 x 10^-3.
867 // Neither Dokken nor Goldapp discusses error for a general
868 // ellipse; Maisonobe does, but his choice of control points
869 // follows different constraints, and Goldapp's expression for
870 // 'alpha' gives much smaller radial error, even for very flat
871 // ellipses, than Maisonobe's equivalent.
873 // For the various corners and for each axis, the sign of this
874 // constant changes, or it might be 0 -- it's multiplied by the
875 // appropriate multiplier from the list before using.
876 const gfxFloat alpha
= 0.55191497064665766025;
878 typedef struct { gfxFloat a
, b
; } twoFloats
;
880 twoFloats cwCornerMults
[4] = { { -1, 0 },
884 twoFloats ccwCornerMults
[4] = { { +1, 0 },
889 twoFloats
*cornerMults
= draw_clockwise
? cwCornerMults
: ccwCornerMults
;
891 gfxPoint pc
, p0
, p1
, p2
, p3
;
894 cairo_move_to(mCairo
, rect
.pos
.x
+ corners
[gfxCorner::TOP_LEFT
].width
, rect
.pos
.y
);
896 cairo_move_to(mCairo
, rect
.pos
.x
+ rect
.size
.width
- corners
[gfxCorner::TOP_RIGHT
].width
, rect
.pos
.y
);
898 for (int i
= 0; i
< gfxCorner::NUM_CORNERS
; i
++) {
899 // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
900 int c
= draw_clockwise
? ((i
+1) % 4) : ((4-i
) % 4);
902 // i+2 and i+3 respectively. These are used to index into the corner
903 // multiplier table, and were deduced by calculating out the long form
904 // of each corner and finding a pattern in the signs and values.
910 if (corners
[c
].width
> 0.0 && corners
[c
].height
> 0.0) {
911 p0
.x
= pc
.x
+ cornerMults
[i
].a
* corners
[c
].width
;
912 p0
.y
= pc
.y
+ cornerMults
[i
].b
* corners
[c
].height
;
914 p3
.x
= pc
.x
+ cornerMults
[i3
].a
* corners
[c
].width
;
915 p3
.y
= pc
.y
+ cornerMults
[i3
].b
* corners
[c
].height
;
917 p1
.x
= p0
.x
+ alpha
* cornerMults
[i2
].a
* corners
[c
].width
;
918 p1
.y
= p0
.y
+ alpha
* cornerMults
[i2
].b
* corners
[c
].height
;
920 p2
.x
= p3
.x
- alpha
* cornerMults
[i3
].a
* corners
[c
].width
;
921 p2
.y
= p3
.y
- alpha
* cornerMults
[i3
].b
* corners
[c
].height
;
923 cairo_line_to (mCairo
, p0
.x
, p0
.y
);
924 cairo_curve_to (mCairo
,
929 cairo_line_to (mCairo
, pc
.x
, pc
.y
);
933 cairo_close_path (mCairo
);