Bug 902591 - Choose the number of linear gradient repetitions in such a way that the whole extents rect is filled. Substantial parts of the code in this patch were written by roc in bug 508730. r=mattwoodrow

--HG--
extra : rebase_source : d7f99adc56ef4d81e14bf3ece6761dd8d5c476ed
This commit is contained in:
Markus Stange 2013-10-11 10:26:40 -04:00
parent 541e6b6e33
commit df71b06fc0

View File

@ -381,6 +381,83 @@ DrawTargetCG::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
return new GradientStopsCG(aStops, aNumStops, aExtendMode);
}
static void
UpdateLinearParametersToIncludePoint(double *min_t, double *max_t,
CGPoint *start,
double dx, double dy,
double x, double y)
{
/**
* Compute a parameter t such that a line perpendicular to the (dx,dy)
* vector, passing through (start->x + dx*t, start->y + dy*t), also
* passes through (x,y).
*
* Let px = x - start->x, py = y - start->y.
* t is given by
* (px - dx*t)*dx + (py - dy*t)*dy = 0
*
* Solving for t we get
* numerator = dx*px + dy*py
* denominator = dx^2 + dy^2
* t = numerator/denominator
*
* In CalculateRepeatingGradientParams we know the length of (dx,dy)
* is not zero. (This is checked in DrawLinearRepeatingGradient.)
*/
double px = x - start->x;
double py = y - start->y;
double numerator = dx * px + dy * py;
double denominator = dx * dx + dy * dy;
double t = numerator / denominator;
if (*min_t > t) {
*min_t = t;
}
if (*max_t < t) {
*max_t = t;
}
}
/**
* Repeat the gradient line such that lines extended perpendicular to the
* gradient line at both start and end would completely enclose the drawing
* extents.
*/
static void
CalculateRepeatingGradientParams(CGPoint *aStart, CGPoint *aEnd,
CGRect aExtents, int *aRepeatCount)
{
double t_min = 0.;
double t_max = 0.;
double dx = aEnd->x - aStart->x;
double dy = aEnd->y - aStart->y;
double bounds_x1 = aExtents.origin.x;
double bounds_y1 = aExtents.origin.y;
double bounds_x2 = aExtents.origin.x + aExtents.size.width;
double bounds_y2 = aExtents.origin.y + aExtents.size.height;
UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
bounds_x1, bounds_y1);
UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
bounds_x2, bounds_y1);
UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
bounds_x2, bounds_y2);
UpdateLinearParametersToIncludePoint(&t_min, &t_max, aStart, dx, dy,
bounds_x1, bounds_y2);
// Move t_min and t_max to the nearest usable integer to try to avoid
// subtle variations due to numerical instability, especially accidentally
// cutting off a pixel. Extending the gradient repetitions is always safe.
t_min = floor (t_min);
t_max = ceil (t_max);
aEnd->x = aStart->x + dx * t_max;
aEnd->y = aStart->y + dy * t_max;
aStart->x = aStart->x + dx * t_min;
aStart->y = aStart->y + dy * t_min;
*aRepeatCount = t_max - t_min;
}
static void
DrawLinearRepeatingGradient(CGContextRef cg, const LinearGradientPattern &aPattern, const CGRect &aExtents)
@ -389,32 +466,12 @@ DrawLinearRepeatingGradient(CGContextRef cg, const LinearGradientPattern &aPatte
CGPoint startPoint = { aPattern.mBegin.x, aPattern.mBegin.y };
CGPoint endPoint = { aPattern.mEnd.x, aPattern.mEnd.y };
// extend the gradient line in multiples of the existing length in both
// directions until it crosses an edge of the extents box.
double xDiff = aPattern.mEnd.x - aPattern.mBegin.x;
double yDiff = aPattern.mEnd.y - aPattern.mBegin.y;
int repeatCount = 1;
// if we don't have a line then we can't extend it
if (xDiff || yDiff) {
while (startPoint.x > aExtents.origin.x
&& startPoint.y > aExtents.origin.y
&& startPoint.x < (aExtents.origin.x+aExtents.size.width)
&& startPoint.y < (aExtents.origin.y+aExtents.size.height))
{
startPoint.x -= xDiff;
startPoint.y -= yDiff;
repeatCount++;
}
while (endPoint.x > aExtents.origin.x
&& endPoint.y > aExtents.origin.y
&& endPoint.x < (aExtents.origin.x+aExtents.size.width)
&& endPoint.y < (aExtents.origin.y+aExtents.size.height))
{
endPoint.x += xDiff;
endPoint.y += yDiff;
repeatCount++;
}
if (aPattern.mEnd.x != aPattern.mBegin.x ||
aPattern.mEnd.y != aPattern.mBegin.y) {
CalculateRepeatingGradientParams(&startPoint, &endPoint, aExtents,
&repeatCount);
}
double scale = 1./repeatCount;