b=424423; border rendering is slow: add APIs to thebes; r=joe

This commit is contained in:
Vladimir Vukicevic 2008-07-23 10:25:00 -07:00
parent 22db57f27e
commit 4098e33bc4
3 changed files with 215 additions and 31 deletions

View File

@ -174,10 +174,15 @@ public:
void LineTo(const gfxPoint& pt);
/**
* Draws a quadratic Bézier curve with control points pt1, pt2 and pt3.
* Draws a cubic Bézier curve with control points pt1, pt2 and pt3.
*/
void CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt3);
/**
* Draws a quadratic Bézier curve with control points pt1, pt2 and pt3.
*/
void QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2);
/**
* Draws a clockwise arc (i.e. a circle segment).
* @param center The center of the circle
@ -210,9 +215,32 @@ public:
* @param snapToPixels ?
*/
void Rectangle(const gfxRect& rect, PRBool snapToPixels = PR_FALSE);
/**
* Draw an ellipse at the center corner with the given dimensions.
* It extends dimensions.width / 2.0 in the horizontal direction
* from the center, and dimensions.height / 2.0 in the vertical
* direction.
*/
void Ellipse(const gfxPoint& center, const gfxSize& dimensions);
/**
* Draw a polygon from the given points
*/
void Polygon(const gfxPoint *points, PRUint32 numPoints);
/*
* Draw a rounded rectangle, with the given outer rect and
* corners. The corners specify the radii of the two axes of an
* ellipse (the horizontal and vertical directions given by the
* width and height, respectively). By default the ellipse is
* drawn in a clockwise direction; if draw_clockwise is PR_FALSE,
* then it's drawn counterclockwise.
*/
void RoundedRectangle(const gfxRect& rect,
const gfxCornerSizes& corners,
PRBool draw_clockwise = PR_TRUE);
/**
** Transformation Matrix manipulation
**/

View File

@ -41,6 +41,19 @@
#include "gfxTypes.h"
#include "gfxPoint.h"
struct THEBES_API gfxCorner {
typedef int Corner;
enum {
// this order is important!
TOP_LEFT = 0,
TOP_RIGHT = 1,
BOTTOM_RIGHT = 2,
BOTTOM_LEFT = 3,
NUM_CORNERS = 4
};
};
struct THEBES_API gfxRect {
// pt? point?
gfxPoint pos;
@ -142,6 +155,19 @@ struct THEBES_API gfxRect {
gfxPoint BottomLeft() const { return pos + gfxSize(0.0, size.height); }
gfxPoint BottomRight() const { return pos + size; }
gfxPoint Corner(gfxCorner::Corner corner) const {
switch (corner) {
case gfxCorner::TOP_LEFT: return TopLeft();
case gfxCorner::TOP_RIGHT: return TopRight();
case gfxCorner::BOTTOM_LEFT: return BottomLeft();
case gfxCorner::BOTTOM_RIGHT: return BottomRight();
default:
NS_ERROR("Invalid corner!");
break;
}
return gfxPoint(0.0, 0.0);
}
/* Conditions this border to Cairo's max coordinate space.
* The caller can check IsEmpty() after Condition() -- if it's TRUE,
* the caller can possibly avoid doing any extra rendering.
@ -174,4 +200,48 @@ struct THEBES_API gfxRect {
}
};
struct THEBES_API gfxCornerSizes {
gfxSize sizes[gfxCorner::NUM_CORNERS];
gfxCornerSizes () { }
gfxCornerSizes (gfxFloat v) {
for (int i = 0; i < gfxCorner::NUM_CORNERS; i++)
sizes[i].SizeTo(v, v);
}
gfxCornerSizes (gfxFloat tl, gfxFloat tr, gfxFloat br, gfxFloat bl) {
sizes[gfxCorner::TOP_LEFT].SizeTo(tl, tl);
sizes[gfxCorner::TOP_RIGHT].SizeTo(tr, tr);
sizes[gfxCorner::BOTTOM_RIGHT].SizeTo(br, br);
sizes[gfxCorner::BOTTOM_LEFT].SizeTo(bl, bl);
}
gfxCornerSizes (const gfxSize& tl, const gfxSize& tr, const gfxSize& br, const gfxSize& bl) {
sizes[gfxCorner::TOP_LEFT] = tl;
sizes[gfxCorner::TOP_RIGHT] = tr;
sizes[gfxCorner::BOTTOM_RIGHT] = br;
sizes[gfxCorner::BOTTOM_LEFT] = bl;
}
const gfxSize& operator[] (gfxCorner::Corner index) const {
return sizes[index];
}
gfxSize& operator[] (gfxCorner::Corner index) {
return sizes[index];
}
const gfxSize TopLeft() const { return sizes[gfxCorner::TOP_LEFT]; }
gfxSize& TopLeft() { return sizes[gfxCorner::TOP_LEFT]; }
const gfxSize TopRight() const { return sizes[gfxCorner::TOP_RIGHT]; }
gfxSize& TopRight() { return sizes[gfxCorner::TOP_RIGHT]; }
const gfxSize BottomLeft() const { return sizes[gfxCorner::BOTTOM_LEFT]; }
gfxSize& BottomLeft() { return sizes[gfxCorner::BOTTOM_LEFT]; }
const gfxSize BottomRight() const { return sizes[gfxCorner::BOTTOM_RIGHT]; }
gfxSize& BottomRight() { return sizes[gfxCorner::BOTTOM_RIGHT]; }
};
#endif /* GFX_RECT_H */

View File

@ -172,6 +172,20 @@ gfxContext::CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt
cairo_curve_to(mCairo, pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
}
void
gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2)
{
double cx, cy;
cairo_get_current_point(mCairo, &cx, &cy);
cairo_curve_to(mCairo,
(cx + pt1.x * 2.0) / 3.0,
(cy + pt1.y * 2.0) / 3.0,
(pt1.x * 2.0 + pt2.x) / 3.0,
(pt1.y * 2.0 + pt2.y) / 3.0,
pt2.x,
pt2.y);
}
void
gfxContext::Arc(const gfxPoint& center, gfxFloat radius,
gfxFloat angle1, gfxFloat angle2)
@ -220,37 +234,11 @@ gfxContext::Rectangle(const gfxRect& rect, PRBool snapToPixels)
void
gfxContext::Ellipse(const gfxPoint& center, const gfxSize& dimensions)
{
// circle?
if (dimensions.width == dimensions.height) {
double radius = dimensions.width / 2.0;
gfxSize halfDim = dimensions / 2.0;
gfxRect r(center - halfDim, dimensions);
gfxCornerSizes c(halfDim, halfDim, halfDim, halfDim);
cairo_arc(mCairo, center.x, center.y, radius, 0, 2.0 * M_PI);
} else {
double x = center.x;
double y = center.y;
double w = dimensions.width;
double h = dimensions.height;
cairo_new_path(mCairo);
cairo_move_to(mCairo, x + w/2.0, y);
cairo_rel_curve_to(mCairo,
0, 0,
w / 2.0, 0,
w / 2.0, h / 2.0);
cairo_rel_curve_to(mCairo,
0, 0,
0, h / 2.0,
- w / 2.0, h / 2.0);
cairo_rel_curve_to(mCairo,
0, 0,
- w / 2.0, 0,
- w / 2.0, - h / 2.0);
cairo_rel_curve_to(mCairo,
0, 0,
0, - h / 2.0,
w / 2.0, - h / 2.0);
}
RoundedRectangle (r, c);
}
void
@ -796,3 +784,101 @@ gfxContext::HasError()
{
return cairo_status(mCairo) != CAIRO_STATUS_SUCCESS;
}
void
gfxContext::RoundedRectangle(const gfxRect& rect,
const gfxCornerSizes& corners,
PRBool draw_clockwise)
{
//
// 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.
//
// For details about representing an elliptical arc as a cubic Bezier curve,
// see http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
//
// 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).
// This is (sqrt(7) - 1) / 3; this ends up falling out of the equations
// given in the above paper -- it's the value of alpha at the end of section
// 3.4.1 when n2 and n1 are 90 degrees apart. For the various corners, the
// axes the sign of this value changes, or it might be 0 -- it's multiplied by
// the appropriate multiplier from the list before using.
const gfxFloat alpha = 0.54858377035486361;
typedef struct { gfxFloat a, b; } twoFloats;
twoFloats cwCornerMults[4] = { { -1, 0 },
{ 0, -1 },
{ +1, 0 },
{ 0, +1 } };
twoFloats ccwCornerMults[4] = { { +1, 0 },
{ 0, -1 },
{ -1, 0 },
{ 0, +1 } };
twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
gfxPoint pc, p0, p1, p2, p3;
if (draw_clockwise)
cairo_move_to(mCairo, rect.pos.x + corners[gfxCorner::TOP_LEFT].width, rect.pos.y);
else
cairo_move_to(mCairo, rect.pos.x + rect.size.width - corners[gfxCorner::TOP_RIGHT].width, rect.pos.y);
for (int i = 0; i < gfxCorner::NUM_CORNERS; i++) {
// the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
int c = draw_clockwise ? ((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 = rect.Corner(c);
if (corners[c].width > 0.0 && corners[c].height > 0.0) {
p0.x = pc.x + cornerMults[i].a * corners[c].width;
p0.y = pc.y + cornerMults[i].b * corners[c].height;
p3.x = pc.x + cornerMults[i3].a * corners[c].width;
p3.y = pc.y + cornerMults[i3].b * corners[c].height;
p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
cairo_line_to (mCairo, p0.x, p0.y);
cairo_curve_to (mCairo,
p1.x, p1.y,
p2.x, p2.y,
p3.x, p3.y);
} else {
cairo_line_to (mCairo, pc.x, pc.y);
}
}
cairo_close_path (mCairo);
}