зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 0863ce40d2f7 (bug 382721)
--HG-- extra : rebase_source : ee6aa275c7ee4291d87a051e92e2e28979b83d96
This commit is contained in:
Родитель
d02a177069
Коммит
8ec9dd1719
|
@ -82,10 +82,6 @@ struct BasePoint {
|
|||
return hypot(x, y);
|
||||
}
|
||||
|
||||
T LengthSquare() const {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
// Round() is *not* rounding to nearest integer if the values are negative.
|
||||
// They are always rounding as floor(n + 0.5).
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
|
||||
|
|
|
@ -1,336 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
// vim:cindent:ts=2:et:sw=2:
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "BezierUtils.h"
|
||||
|
||||
#include "PathHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
Point
|
||||
GetBezierPoint(const Bezier& aBezier, Float t)
|
||||
{
|
||||
Float s = 1.0f - t;
|
||||
|
||||
return Point(
|
||||
aBezier.mPoints[0].x * s * s * s +
|
||||
3.0f * aBezier.mPoints[1].x * t * s * s +
|
||||
3.0f * aBezier.mPoints[2].x * t * t * s +
|
||||
aBezier.mPoints[3].x * t * t * t,
|
||||
aBezier.mPoints[0].y * s * s * s +
|
||||
3.0f * aBezier.mPoints[1].y * t * s * s +
|
||||
3.0f * aBezier.mPoints[2].y * t * t * s +
|
||||
aBezier.mPoints[3].y * t * t * t
|
||||
);
|
||||
}
|
||||
|
||||
Point
|
||||
GetBezierDifferential(const Bezier& aBezier, Float t)
|
||||
{
|
||||
// Return P'(t).
|
||||
|
||||
Float s = 1.0f - t;
|
||||
|
||||
return Point(
|
||||
-3.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s * s +
|
||||
2.0f * (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * t * s +
|
||||
(aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t * t),
|
||||
-3.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s * s +
|
||||
2.0f * (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * t * s+
|
||||
(aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t * t)
|
||||
);
|
||||
}
|
||||
|
||||
Point
|
||||
GetBezierDifferential2(const Bezier& aBezier, Float t)
|
||||
{
|
||||
// Return P''(t).
|
||||
|
||||
Float s = 1.0f - t;
|
||||
|
||||
return Point(
|
||||
6.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s -
|
||||
(aBezier.mPoints[1].x - aBezier.mPoints[2].x) * (s - t) -
|
||||
(aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t),
|
||||
6.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s -
|
||||
(aBezier.mPoints[1].y - aBezier.mPoints[2].y) * (s - t) -
|
||||
(aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t)
|
||||
);
|
||||
}
|
||||
|
||||
Float
|
||||
GetBezierLength(const Bezier& aBezier, Float a, Float b)
|
||||
{
|
||||
if (a < 0.5f && b > 0.5f) {
|
||||
// To increase the accuracy, split into two parts.
|
||||
return GetBezierLength(aBezier, a, 0.5f) +
|
||||
GetBezierLength(aBezier, 0.5f, b);
|
||||
}
|
||||
|
||||
// Calculate length of simple bezier curve with Simpson's rule.
|
||||
// _
|
||||
// / b
|
||||
// length = | |P'(x)| dx
|
||||
// _/ a
|
||||
//
|
||||
// b - a a + b
|
||||
// = ----- [ |P'(a)| + 4 |P'(-----)| + |P'(b)| ]
|
||||
// 6 2
|
||||
|
||||
Float fa = GetBezierDifferential(aBezier, a).Length();
|
||||
Float fab = GetBezierDifferential(aBezier, (a + b) / 2.0f).Length();
|
||||
Float fb = GetBezierDifferential(aBezier, b).Length();
|
||||
|
||||
return (b - a) / 6.0f * (fa + 4.0f * fab + fb);
|
||||
}
|
||||
|
||||
static void
|
||||
SplitBezierA(Bezier* aSubBezier, const Bezier& aBezier, Float t)
|
||||
{
|
||||
// Split bezier curve into [0,t] and [t,1] parts, and return [0,t] part.
|
||||
|
||||
Float s = 1.0f - t;
|
||||
|
||||
Point tmp1;
|
||||
Point tmp2;
|
||||
|
||||
aSubBezier->mPoints[0] = aBezier.mPoints[0];
|
||||
|
||||
aSubBezier->mPoints[1] = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
|
||||
tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
|
||||
tmp2 = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
|
||||
|
||||
aSubBezier->mPoints[2] = aSubBezier->mPoints[1] * s + tmp1 * t;
|
||||
tmp1 = tmp1 * s + tmp2 * t;
|
||||
|
||||
aSubBezier->mPoints[3] = aSubBezier->mPoints[2] * s + tmp1 * t;
|
||||
}
|
||||
|
||||
static void
|
||||
SplitBezierB(Bezier* aSubBezier, const Bezier& aBezier, Float t)
|
||||
{
|
||||
// Split bezier curve into [0,t] and [t,1] parts, and return [t,1] part.
|
||||
|
||||
Float s = 1.0f - t;
|
||||
|
||||
Point tmp1;
|
||||
Point tmp2;
|
||||
|
||||
aSubBezier->mPoints[3] = aBezier.mPoints[3];
|
||||
|
||||
aSubBezier->mPoints[2] = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
|
||||
tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
|
||||
tmp2 = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
|
||||
|
||||
aSubBezier->mPoints[1] = tmp1 * s + aSubBezier->mPoints[2] * t;
|
||||
tmp1 = tmp2 * s + tmp1 * t;
|
||||
|
||||
aSubBezier->mPoints[0] = tmp1 * s + aSubBezier->mPoints[1] * t;
|
||||
}
|
||||
|
||||
void
|
||||
GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier, Float t1, Float t2)
|
||||
{
|
||||
Bezier tmp;
|
||||
SplitBezierB(&tmp, aBezier, t1);
|
||||
|
||||
Float range = 1.0f - t1;
|
||||
if (range == 0.0f) {
|
||||
*aSubBezier = tmp;
|
||||
} else {
|
||||
SplitBezierA(aSubBezier, tmp, (t2 - t1) / range);
|
||||
}
|
||||
}
|
||||
|
||||
static Point
|
||||
BisectBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
|
||||
Float* aT)
|
||||
{
|
||||
// Find a nearest point on bezier curve with Binary search.
|
||||
// Called from FindBezierNearestPoint.
|
||||
|
||||
Float lower = 0.0f;
|
||||
Float upper = 1.0f;
|
||||
Float t;
|
||||
|
||||
Point P, lastP;
|
||||
const size_t MAX_LOOP = 32;
|
||||
const Float DIST_MARGIN = 0.1f;
|
||||
const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
|
||||
const Float DIFF = 0.0001f;
|
||||
for (size_t i = 0; i < MAX_LOOP; i++) {
|
||||
t = (upper + lower) / 2.0f;
|
||||
P = GetBezierPoint(aBezier, t);
|
||||
|
||||
// Check if it converged.
|
||||
if (i > 0 && (lastP - P).LengthSquare() < DIST_MARGIN_SQUARE) {
|
||||
break;
|
||||
}
|
||||
|
||||
Float distSquare = (P - aTarget).LengthSquare();
|
||||
if ((GetBezierPoint(aBezier, t + DIFF) - aTarget).LengthSquare() <
|
||||
distSquare) {
|
||||
lower = t;
|
||||
} else if ((GetBezierPoint(aBezier, t - DIFF) - aTarget).LengthSquare() <
|
||||
distSquare) {
|
||||
upper = t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
lastP = P;
|
||||
}
|
||||
|
||||
if (aT) {
|
||||
*aT = t;
|
||||
}
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
Point
|
||||
FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
|
||||
Float aInitialT, Float* aT)
|
||||
{
|
||||
// Find a nearest point on bezier curve with Newton's method.
|
||||
// It converges within 4 iterations in most cases.
|
||||
//
|
||||
// f(t_n)
|
||||
// t_{n+1} = t_n - ---------
|
||||
// f'(t_n)
|
||||
//
|
||||
// d 2
|
||||
// f(t) = ---- | P(t) - aTarget |
|
||||
// dt
|
||||
|
||||
Float t = aInitialT;
|
||||
Point P;
|
||||
Point lastP = GetBezierPoint(aBezier, t);
|
||||
|
||||
const size_t MAX_LOOP = 4;
|
||||
const Float DIST_MARGIN = 0.1f;
|
||||
const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
|
||||
for (size_t i = 0; i <= MAX_LOOP; i++) {
|
||||
Point dP = GetBezierDifferential(aBezier, t);
|
||||
Point ddP = GetBezierDifferential2(aBezier, t);
|
||||
Float f = 2.0f * (lastP.DotProduct(dP) - aTarget.DotProduct(dP));
|
||||
Float df = 2.0f * (dP.DotProduct(dP) + lastP.DotProduct(ddP) -
|
||||
aTarget.DotProduct(ddP));
|
||||
t = t - f / df;
|
||||
P = GetBezierPoint(aBezier, t);
|
||||
if ((P - lastP).LengthSquare() < DIST_MARGIN_SQUARE) {
|
||||
break;
|
||||
}
|
||||
lastP = P;
|
||||
|
||||
if (i == MAX_LOOP) {
|
||||
// If aInitialT is too bad, it won't converge in a few iterations,
|
||||
// fallback to binary search.
|
||||
return BisectBezierNearestPoint(aBezier, aTarget, aT);
|
||||
}
|
||||
}
|
||||
|
||||
if (aT) {
|
||||
*aT = t;
|
||||
}
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
void
|
||||
GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
|
||||
const Point& aCornerPoint, const Size& aCornerSize)
|
||||
{
|
||||
// Calculate bezier control points for elliptic arc.
|
||||
|
||||
const Float signsList[4][2] = {
|
||||
{ +1.0f, +1.0f },
|
||||
{ -1.0f, +1.0f },
|
||||
{ -1.0f, -1.0f },
|
||||
{ +1.0f, -1.0f }
|
||||
};
|
||||
const Float (& signs)[2] = signsList[aCorner];
|
||||
|
||||
aBezier->mPoints[0] = aCornerPoint;
|
||||
aBezier->mPoints[0].x += signs[0] * aCornerSize.width;
|
||||
|
||||
aBezier->mPoints[1] = aBezier->mPoints[0];
|
||||
aBezier->mPoints[1].x -= signs[0] * aCornerSize.width * kKappaFactor;
|
||||
|
||||
aBezier->mPoints[3] = aCornerPoint;
|
||||
aBezier->mPoints[3].y += signs[1] * aCornerSize.height;
|
||||
|
||||
aBezier->mPoints[2] = aBezier->mPoints[3];
|
||||
aBezier->mPoints[2].y -= signs[1] * aCornerSize.height * kKappaFactor;
|
||||
}
|
||||
|
||||
Float
|
||||
GetQuarterEllipticArcLength(Float a, Float b)
|
||||
{
|
||||
// Calculate the approximate length of a quarter elliptic arc formed by radii
|
||||
// (a, b), by Ramanujan's approximation of the perimeter p of an ellipse.
|
||||
// _ _
|
||||
// | 2 |
|
||||
// | 3 * (a - b) |
|
||||
// p = PI | (a + b) + ------------------------------------------- |
|
||||
// | 2 2 |
|
||||
// |_ 10 * (a + b) + sqrt(a + 14 * a * b + b ) _|
|
||||
//
|
||||
// _ _
|
||||
// | 2 |
|
||||
// | 3 * (a - b) |
|
||||
// = PI | (a + b) + -------------------------------------------------- |
|
||||
// | 2 2 |
|
||||
// |_ 10 * (a + b) + sqrt(4 * (a + b) - 3 * (a - b) ) _|
|
||||
//
|
||||
// _ _
|
||||
// | 2 |
|
||||
// | 3 * S |
|
||||
// = PI | A + -------------------------------------- |
|
||||
// | 2 2 |
|
||||
// |_ 10 * A + sqrt(4 * A - 3 * S ) _|
|
||||
//
|
||||
// where A = a + b, S = a - b
|
||||
|
||||
Float A = a + b, S = a - b;
|
||||
Float A2 = A * A, S2 = S * S;
|
||||
Float p = M_PI * (A + 3.0f * S2 / (10.0f * A + sqrt(4.0f * A2 - 3.0f * S2)));
|
||||
return p / 4.0f;
|
||||
}
|
||||
|
||||
Float
|
||||
CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
|
||||
const Point& origin, Float width, Float height)
|
||||
{
|
||||
// Solve following equations with n and return smaller n.
|
||||
//
|
||||
// / (x, y) = P + n * normal
|
||||
// |
|
||||
// < _ _ 2 _ _ 2
|
||||
// | | x - origin.x | | y - origin.y |
|
||||
// | | ------------ | + | ------------ | = 1
|
||||
// \ |_ width _| |_ height _|
|
||||
|
||||
Float a = (P.x - origin.x) / width;
|
||||
Float b = normal.x / width;
|
||||
Float c = (P.y - origin.y) / height;
|
||||
Float d = normal.y / height;
|
||||
|
||||
Float A = b * b + d * d;
|
||||
Float B = a * b + c * d;
|
||||
Float C = a * a + c * c - 1;
|
||||
|
||||
Float S = sqrt(B * B - A * C);
|
||||
|
||||
Float n1 = (- B + S) / A;
|
||||
Float n2 = (- B - S) / A;
|
||||
|
||||
return n1 < n2 ? n1 : n2;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
|
@ -1,185 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_BezierUtils_h_
|
||||
#define mozilla_BezierUtils_h_
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "gfxRect.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// Control points for bezier curve
|
||||
//
|
||||
// mPoints[2]
|
||||
// +-----___---+ mPoints[3]
|
||||
// __--
|
||||
// _--
|
||||
// /
|
||||
// /
|
||||
// mPoints[1] + |
|
||||
// | |
|
||||
// ||
|
||||
// ||
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// mPoints[0] +
|
||||
struct Bezier {
|
||||
Point mPoints[4];
|
||||
};
|
||||
|
||||
// Calculate a point or it's differential of a bezier curve formed by
|
||||
// aBezier and parameter t.
|
||||
//
|
||||
// GetBezierPoint = P(t)
|
||||
// GetBezierDifferential = P'(t)
|
||||
// GetBezierDifferential2 = P''(t)
|
||||
//
|
||||
// mPoints[2]
|
||||
// +-----___---+ mPoints[3]
|
||||
// __-- P(1)
|
||||
// _--
|
||||
// +
|
||||
// / P(t)
|
||||
// mPoints[1] + |
|
||||
// | |
|
||||
// ||
|
||||
// ||
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// mPoints[0] + P(0)
|
||||
Point GetBezierPoint(const Bezier& aBezier, Float t);
|
||||
Point GetBezierDifferential(const Bezier& aBezier, Float t);
|
||||
Point GetBezierDifferential2(const Bezier& aBezier, Float t);
|
||||
|
||||
// Calculate length of a simple bezier curve formed by aBezier and range [a, b].
|
||||
Float GetBezierLength(const Bezier& aBezier, Float a, Float b);
|
||||
|
||||
// Split bezier curve formed by aBezier into [0,t1], [t1,t2], [t2,1] parts, and
|
||||
// stores control points for [t1,t2] to aSubBezier.
|
||||
//
|
||||
// ___---+
|
||||
// __+- P(1)
|
||||
// _-- P(t2)
|
||||
// -
|
||||
// / <-- aSubBezier
|
||||
// |
|
||||
// |
|
||||
// +
|
||||
// | P(t1)
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// + P(0)
|
||||
void GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier,
|
||||
Float t1, Float t2);
|
||||
|
||||
// Find a nearest point on bezier curve formed by aBezier to a point aTarget.
|
||||
// aInitialT is a hint to find the parameter t for the nearest point.
|
||||
// If aT is non-null, parameter for the nearest point is stored to *aT.
|
||||
// This function expects a bezier curve to be an approximation of elliptic arc.
|
||||
// Otherwise it will return wrong point.
|
||||
//
|
||||
// aTarget
|
||||
// + ___---+
|
||||
// __--
|
||||
// _--
|
||||
// +
|
||||
// / nearest point = P(t = *aT)
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// + P(aInitialT)
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// +
|
||||
Point FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
|
||||
Float aInitialT, Float* aT=nullptr);
|
||||
|
||||
// Calculate control points for a bezier curve that is an approximation of
|
||||
// an elliptic arc.
|
||||
//
|
||||
// aCornerSize.width
|
||||
// |<----------------->|
|
||||
// | |
|
||||
// aCornerPoint| mPoints[2] |
|
||||
// -------------+-------+-----___---+ mPoints[3]
|
||||
// ^ | __--
|
||||
// | | _--
|
||||
// | | -
|
||||
// | | /
|
||||
// aCornerSize.height | mPoints[1] + |
|
||||
// | | |
|
||||
// | ||
|
||||
// | ||
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// v mPoints[0] |
|
||||
// -------------+
|
||||
void GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
|
||||
const Point& aCornerPoint,
|
||||
const Size& aCornerSize);
|
||||
|
||||
// Calculate the approximate length of a quarter elliptic arc formed by radii
|
||||
// (a, b).
|
||||
//
|
||||
// a
|
||||
// |<----------------->|
|
||||
// | |
|
||||
// ---+-------------___---+
|
||||
// ^ | __--
|
||||
// | | _--
|
||||
// | | -
|
||||
// | | /
|
||||
// b | | |
|
||||
// | | |
|
||||
// | ||
|
||||
// | ||
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// v |
|
||||
// ---+
|
||||
Float GetQuarterEllipticArcLength(Float a, Float b);
|
||||
|
||||
// Calculate the distance between an elliptic arc formed by (origin, width,
|
||||
// height), and a point P, along a line formed by |P + n * normal|.
|
||||
// P should be outside of the ellipse, and the line should cross with the
|
||||
// ellipse twice at n > 0 points.
|
||||
//
|
||||
// width
|
||||
// |<----------------->|
|
||||
// origin | |
|
||||
// -----------+-------------___---+
|
||||
// ^ normal | __--
|
||||
// | P +->__ | _--
|
||||
// | --__ -
|
||||
// | | --+
|
||||
// height | | |
|
||||
// | | |
|
||||
// | ||
|
||||
// | ||
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// v |
|
||||
// -----------+
|
||||
Float CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
|
||||
const Point& origin,
|
||||
Float width, Float height);
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_BezierUtils_h_ */
|
|
@ -17,7 +17,6 @@ EXPORTS.mozilla.gfx += [
|
|||
'BasePoint4D.h',
|
||||
'BaseRect.h',
|
||||
'BaseSize.h',
|
||||
'BezierUtils.h',
|
||||
'Blur.h',
|
||||
'BorrowedContext.h',
|
||||
'Coord.h',
|
||||
|
@ -139,7 +138,6 @@ elif CONFIG['CPU_ARCH'].startswith('mips'):
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'BezierUtils.cpp',
|
||||
'Blur.cpp',
|
||||
'DataSourceSurface.cpp',
|
||||
'DataSurfaceHelpers.cpp',
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_BorderCache_h_
|
||||
#define mozilla_BorderCache_h_
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "PLDHashTable.h"
|
||||
|
||||
namespace mozilla {
|
||||
// Cache for best overlap and best dashLength.
|
||||
|
||||
struct FourFloats
|
||||
{
|
||||
typedef mozilla::gfx::Float Float;
|
||||
|
||||
Float n[4];
|
||||
|
||||
FourFloats()
|
||||
{
|
||||
n[0] = 0.0f;
|
||||
n[1] = 0.0f;
|
||||
n[2] = 0.0f;
|
||||
n[3] = 0.0f;
|
||||
}
|
||||
|
||||
FourFloats(Float a, Float b, Float c, Float d)
|
||||
{
|
||||
n[0] = a;
|
||||
n[1] = b;
|
||||
n[2] = c;
|
||||
n[3] = d;
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const FourFloats& aOther) const
|
||||
{
|
||||
return n[0] == aOther.n[0] &&
|
||||
n[1] == aOther.n[1] &&
|
||||
n[2] == aOther.n[2] &&
|
||||
n[3] == aOther.n[3];
|
||||
}
|
||||
};
|
||||
|
||||
class FourFloatsHashKey : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
typedef const FourFloats& KeyType;
|
||||
typedef const FourFloats* KeyTypePointer;
|
||||
|
||||
explicit FourFloatsHashKey(KeyTypePointer aKey) : mValue(*aKey) {}
|
||||
FourFloatsHashKey(const FourFloatsHashKey& aToCopy) : mValue(aToCopy.mValue) {}
|
||||
~FourFloatsHashKey() {}
|
||||
|
||||
KeyType GetKey() const { return mValue; }
|
||||
bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
|
||||
static PLDHashNumber HashKey(KeyTypePointer aKey)
|
||||
{
|
||||
return HashBytes(aKey->n, sizeof(mozilla::gfx::Float) * 4);
|
||||
}
|
||||
enum { ALLOW_MEMMOVE = true };
|
||||
|
||||
private:
|
||||
const FourFloats mValue;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_BorderCache_h_ */
|
|
@ -1,427 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DashedCornerFinder.h"
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
#include "BorderCache.h"
|
||||
#include "BorderConsts.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
struct BestDashLength
|
||||
{
|
||||
typedef mozilla::gfx::Float Float;
|
||||
|
||||
Float dashLength;
|
||||
size_t count;
|
||||
|
||||
BestDashLength()
|
||||
: dashLength(0.0f), count(0)
|
||||
{}
|
||||
|
||||
BestDashLength(Float aDashLength, size_t aCount)
|
||||
: dashLength(aDashLength), count(aCount)
|
||||
{}
|
||||
};
|
||||
|
||||
static const size_t DashedCornerCacheSize = 256;
|
||||
nsDataHashtable<FourFloatsHashKey, BestDashLength> DashedCornerCache;
|
||||
|
||||
DashedCornerFinder::DashedCornerFinder(const Bezier& aOuterBezier,
|
||||
const Bezier& aInnerBezier,
|
||||
Float aBorderWidthH, Float aBorderWidthV,
|
||||
const Size& aCornerDim)
|
||||
: mOuterBezier(aOuterBezier),
|
||||
mInnerBezier(aInnerBezier),
|
||||
mLastOuterP(aOuterBezier.mPoints[0]), mLastInnerP(aInnerBezier.mPoints[0]),
|
||||
mLastOuterT(0.0f), mLastInnerT(0.0f),
|
||||
mBestDashLength(DOT_LENGTH * DASH_LENGTH),
|
||||
mHasZeroBorderWidth(false), mHasMore(true),
|
||||
mMaxCount(aCornerDim.width + aCornerDim.height),
|
||||
mType(OTHER),
|
||||
mI(0), mCount(0)
|
||||
{
|
||||
NS_ASSERTION(aBorderWidthH > 0.0f || aBorderWidthV > 0.0f,
|
||||
"At least one side should have non-zero width.");
|
||||
|
||||
DetermineType(aBorderWidthH, aBorderWidthV);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
DashedCornerFinder::DetermineType(Float aBorderWidthH, Float aBorderWidthV)
|
||||
{
|
||||
if (aBorderWidthH < aBorderWidthV) {
|
||||
// Always draw from wider side to thinner side.
|
||||
Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]);
|
||||
Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]);
|
||||
Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]);
|
||||
Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]);
|
||||
mLastOuterP = mOuterBezier.mPoints[0];
|
||||
mLastInnerP = mInnerBezier.mPoints[0];
|
||||
}
|
||||
|
||||
// See the comment at mType declaration for each condition.
|
||||
|
||||
Float borderRadiusA = fabs(mOuterBezier.mPoints[0].x -
|
||||
mOuterBezier.mPoints[3].x);
|
||||
Float borderRadiusB = fabs(mOuterBezier.mPoints[0].y -
|
||||
mOuterBezier.mPoints[3].y);
|
||||
if (aBorderWidthH == aBorderWidthV &&
|
||||
borderRadiusA == borderRadiusB &&
|
||||
borderRadiusA > aBorderWidthH * 2.0f) {
|
||||
Float curveHeight = borderRadiusA - aBorderWidthH / 2.0;
|
||||
|
||||
mType = PERFECT;
|
||||
Float borderLength = M_PI * curveHeight / 2.0f;
|
||||
|
||||
Float dashWidth = aBorderWidthH * DOT_LENGTH * DASH_LENGTH;
|
||||
size_t count = ceil(borderLength / dashWidth);
|
||||
if (count % 2) {
|
||||
count++;
|
||||
}
|
||||
mCount = count / 2 + 1;
|
||||
mBestDashLength = borderLength / (aBorderWidthH * count);
|
||||
}
|
||||
|
||||
Float minBorderWidth = std::min(aBorderWidthH, aBorderWidthV);
|
||||
if (minBorderWidth == 0.0f) {
|
||||
mHasZeroBorderWidth = true;
|
||||
}
|
||||
|
||||
if (mType == OTHER && !mHasZeroBorderWidth) {
|
||||
Float minBorderRadius = std::min(borderRadiusA, borderRadiusB);
|
||||
Float maxBorderRadius = std::max(borderRadiusA, borderRadiusB);
|
||||
Float maxBorderWidth = std::max(aBorderWidthH, aBorderWidthV);
|
||||
|
||||
FindBestDashLength(minBorderWidth, maxBorderWidth,
|
||||
minBorderRadius, maxBorderRadius);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DashedCornerFinder::HasMore(void) const
|
||||
{
|
||||
if (mHasZeroBorderWidth) {
|
||||
return mI < mMaxCount && mHasMore;
|
||||
}
|
||||
|
||||
return mI < mCount;
|
||||
}
|
||||
|
||||
DashedCornerFinder::Result
|
||||
DashedCornerFinder::Next(void)
|
||||
{
|
||||
Float lastOuterT, lastInnerT, outerT, innerT;
|
||||
|
||||
if (mI == 0) {
|
||||
lastOuterT = 0.0f;
|
||||
lastInnerT = 0.0f;
|
||||
} else {
|
||||
if (mType == PERFECT) {
|
||||
lastOuterT = lastInnerT = (mI * 2.0f - 0.5f) / ((mCount - 1) * 2.0f);
|
||||
} else {
|
||||
Float last2OuterT = mLastOuterT;
|
||||
Float last2InnerT = mLastInnerT;
|
||||
|
||||
(void)FindNext(mBestDashLength);
|
||||
|
||||
//
|
||||
// mLastOuterT lastOuterT
|
||||
// | |
|
||||
// v v
|
||||
// +---+---+---+---+ <- last2OuterT
|
||||
// | |###|###| |
|
||||
// | |###|###| |
|
||||
// | |###|###| |
|
||||
// +---+---+---+---+ <- last2InnerT
|
||||
// ^ ^
|
||||
// | |
|
||||
// mLastInnerT lastInnerT
|
||||
lastOuterT = (mLastOuterT + last2OuterT) / 2.0f;
|
||||
lastInnerT = (mLastInnerT + last2InnerT) / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!mHasZeroBorderWidth && mI == mCount - 1) ||
|
||||
(mHasZeroBorderWidth && !mHasMore)) {
|
||||
outerT = 1.0f;
|
||||
innerT = 1.0f;
|
||||
} else {
|
||||
if (mType == PERFECT) {
|
||||
outerT = innerT = (mI * 2.0f + 0.5f) / ((mCount - 1) * 2.0f);
|
||||
} else {
|
||||
Float last2OuterT = mLastOuterT;
|
||||
Float last2InnerT = mLastInnerT;
|
||||
|
||||
(void)FindNext(mBestDashLength);
|
||||
|
||||
//
|
||||
// outerT last2OuterT
|
||||
// | |
|
||||
// v v
|
||||
// mLastOuterT -> +---+---+---+---+
|
||||
// | |###|###| |
|
||||
// | |###|###| |
|
||||
// | |###|###| |
|
||||
// mLastInnerT -> +---+---+---+---+
|
||||
// ^ ^
|
||||
// | |
|
||||
// innerT last2InnerT
|
||||
outerT = (mLastOuterT + last2OuterT) / 2.0f;
|
||||
innerT = (mLastInnerT + last2InnerT) / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
mI++;
|
||||
|
||||
Bezier outerSectionBezier;
|
||||
Bezier innerSectionBezier;
|
||||
GetSubBezier(&outerSectionBezier, mOuterBezier, lastOuterT, outerT);
|
||||
GetSubBezier(&innerSectionBezier, mInnerBezier, lastInnerT, innerT);
|
||||
return DashedCornerFinder::Result(outerSectionBezier, innerSectionBezier);
|
||||
}
|
||||
|
||||
void
|
||||
DashedCornerFinder::Reset(void)
|
||||
{
|
||||
mLastOuterP = mOuterBezier.mPoints[0];
|
||||
mLastInnerP = mInnerBezier.mPoints[0];
|
||||
mLastOuterT = 0.0f;
|
||||
mLastInnerT = 0.0f;
|
||||
mHasMore = true;
|
||||
}
|
||||
|
||||
Float
|
||||
DashedCornerFinder::FindNext(Float dashLength)
|
||||
{
|
||||
Float upper = 1.0f;
|
||||
Float lower = mLastOuterT;
|
||||
|
||||
Point OuterP, InnerP;
|
||||
// Start from upper bound to check if this is the last segment.
|
||||
Float outerT = upper;
|
||||
Float innerT;
|
||||
Float W = 0.0f;
|
||||
Float L = 0.0f;
|
||||
|
||||
const Float LENGTH_MARGIN = 0.1f;
|
||||
for (size_t i = 0; i < MAX_LOOP; i++) {
|
||||
OuterP = GetBezierPoint(mOuterBezier, outerT);
|
||||
InnerP = FindBezierNearestPoint(mInnerBezier, OuterP, outerT, &innerT);
|
||||
|
||||
// Calculate approximate dash length.
|
||||
//
|
||||
// W = (W1 + W2) / 2
|
||||
// L = (OuterL + InnerL) / 2
|
||||
// dashLength = L / W
|
||||
//
|
||||
// ____----+----____
|
||||
// OuterP ___--- | ---___ mLastOuterP
|
||||
// +--- | ---+
|
||||
// | | |
|
||||
// | | |
|
||||
// | W | W1 |
|
||||
// | | |
|
||||
// W2 | | |
|
||||
// | | ______------+
|
||||
// | ____+---- mLastInnerP
|
||||
// | ___---
|
||||
// | __---
|
||||
// +--
|
||||
// InnerP
|
||||
// OuterL
|
||||
// ____---------____
|
||||
// OuterP ___--- ---___ mLastOuterP
|
||||
// +--- ---+
|
||||
// | L |
|
||||
// | ___----------______ |
|
||||
// | __--- -----+
|
||||
// | __-- |
|
||||
// +-- |
|
||||
// | InnerL ______------+
|
||||
// | ____----- mLastInnerP
|
||||
// | ___---
|
||||
// | __---
|
||||
// +--
|
||||
// InnerP
|
||||
Float W1 = (mLastOuterP - mLastInnerP).Length();
|
||||
Float W2 = (OuterP - InnerP).Length();
|
||||
Float OuterL = GetBezierLength(mOuterBezier, mLastOuterT, outerT);
|
||||
Float InnerL = GetBezierLength(mInnerBezier, mLastInnerT, innerT);
|
||||
W = (W1 + W2) / 2.0f;
|
||||
L = (OuterL + InnerL) / 2.0f;
|
||||
if (L > W * dashLength + LENGTH_MARGIN) {
|
||||
if (i > 0) {
|
||||
upper = outerT;
|
||||
}
|
||||
} else if (L < W * dashLength - LENGTH_MARGIN) {
|
||||
if (i == 0) {
|
||||
// This is the last segment with shorter dashLength.
|
||||
mHasMore = false;
|
||||
break;
|
||||
}
|
||||
lower = outerT;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
outerT = (upper + lower) / 2.0f;
|
||||
}
|
||||
|
||||
mLastOuterP = OuterP;
|
||||
mLastInnerP = InnerP;
|
||||
mLastOuterT = outerT;
|
||||
mLastInnerT = innerT;
|
||||
|
||||
if (W == 0.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return L / W;
|
||||
}
|
||||
|
||||
void
|
||||
DashedCornerFinder::FindBestDashLength(Float aMinBorderWidth,
|
||||
Float aMaxBorderWidth,
|
||||
Float aMinBorderRadius,
|
||||
Float aMaxBorderRadius)
|
||||
{
|
||||
// If dashLength is not calculateable, find it with binary search,
|
||||
// such that there exists i that OuterP_i == OuterP_n and
|
||||
// InnerP_i == InnerP_n with given dashLength.
|
||||
|
||||
FourFloats key(aMinBorderWidth, aMaxBorderWidth,
|
||||
aMinBorderRadius, aMaxBorderRadius);
|
||||
BestDashLength best;
|
||||
if (DashedCornerCache.Get(key, &best)) {
|
||||
mCount = best.count;
|
||||
mBestDashLength = best.dashLength;
|
||||
return;
|
||||
}
|
||||
|
||||
Float lower = 1.0f;
|
||||
Float upper = DOT_LENGTH * DASH_LENGTH;
|
||||
Float dashLength = upper;
|
||||
size_t targetCount = 0;
|
||||
|
||||
const Float LENGTH_MARGIN = 0.1f;
|
||||
for (size_t j = 0; j < MAX_LOOP; j++) {
|
||||
size_t count;
|
||||
Float actualDashLength;
|
||||
if (!GetCountAndLastDashLength(dashLength, &count, &actualDashLength)) {
|
||||
if (j == 0) {
|
||||
mCount = mMaxCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
if (count == 1) {
|
||||
// If only 1 segment fits, fill entire region
|
||||
//
|
||||
// count = 1
|
||||
// mCount = 1
|
||||
// | 1 |
|
||||
// +---+---+
|
||||
// |###|###|
|
||||
// |###|###|
|
||||
// |###|###|
|
||||
// +---+---+
|
||||
// 1
|
||||
mCount = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// targetCount should be 2n.
|
||||
//
|
||||
// targetCount = 2
|
||||
// mCount = 2
|
||||
// | 1 | 2 |
|
||||
// +---+---+---+---+
|
||||
// |###| | |###|
|
||||
// |###| | |###|
|
||||
// |###| | |###|
|
||||
// +---+---+---+---+
|
||||
// 1 2
|
||||
//
|
||||
// targetCount = 6
|
||||
// mCount = 4
|
||||
// | 1 | 2 | 3 | 4 | 5 | 6 |
|
||||
// +---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
// |###| | |###|###| | |###|###| | |###|
|
||||
// |###| | |###|###| | |###|###| | |###|
|
||||
// |###| | |###|###| | |###|###| | |###|
|
||||
// +---+---+---+---+---+---+---+---+---+---+---+---+
|
||||
// 1 2 3 4
|
||||
if (count % 2) {
|
||||
targetCount = count + 1;
|
||||
} else {
|
||||
targetCount = count;
|
||||
}
|
||||
|
||||
mCount = targetCount / 2 + 1;
|
||||
}
|
||||
|
||||
if (count == targetCount) {
|
||||
mBestDashLength = dashLength;
|
||||
|
||||
// actualDashLength won't be greater than dashLength.
|
||||
if (actualDashLength > dashLength - LENGTH_MARGIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We started from upper bound, no need to update range when j == 0.
|
||||
if (j > 0) {
|
||||
upper = dashLength;
|
||||
}
|
||||
} else {
|
||||
// |j == 0 && count != targetCount| means that |targetCount = count + 1|,
|
||||
// and we started from upper bound, no need to update range when j == 0.
|
||||
if (j > 0) {
|
||||
if (count > targetCount) {
|
||||
lower = dashLength;
|
||||
} else {
|
||||
upper = dashLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dashLength = (upper + lower) / 2.0f;
|
||||
}
|
||||
|
||||
if (DashedCornerCache.Count() > DashedCornerCacheSize) {
|
||||
DashedCornerCache.Clear();
|
||||
}
|
||||
DashedCornerCache.Put(key, BestDashLength(mBestDashLength, mCount));
|
||||
}
|
||||
|
||||
bool
|
||||
DashedCornerFinder::GetCountAndLastDashLength(Float aDashLength,
|
||||
size_t* aCount,
|
||||
Float* aActualDashLength)
|
||||
{
|
||||
// Return the number of segments and the last segment's dashLength for
|
||||
// the given dashLength.
|
||||
|
||||
Reset();
|
||||
|
||||
for (size_t i = 0; i < mMaxCount; i++) {
|
||||
Float actualDashLength = FindNext(aDashLength);
|
||||
if (mLastOuterT >= 1.0f) {
|
||||
*aCount = i + 1;
|
||||
*aActualDashLength = actualDashLength;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,277 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_DashedCornerFinder_h_
|
||||
#define mozilla_DashedCornerFinder_h_
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/BezierUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Calculate {OuterT_i, InnerT_i} for each 1 < i < n, that
|
||||
// (OuterL_i + InnerL_i) / 2 == dashLength * (W_i + W_{i-1}) / 2
|
||||
// where
|
||||
// OuterP_i: OuterCurve(OuterT_i)
|
||||
// InnerP_i: InnerCurve(OuterT_i)
|
||||
// OuterL_i: Elliptic arc length between OuterP_i - OuterP_{i-1}
|
||||
// InnerL_i: Elliptic arc length between InnerP_i - InnerP_{i-1}
|
||||
// W_i = |OuterP_i - InnerP_i|
|
||||
// 1.0 < dashLength < 3.0
|
||||
//
|
||||
// OuterP_1 OuterP_0
|
||||
// _+__-----------+ OuterCurve
|
||||
// OuterP_2 __---- | OuterL_1 |
|
||||
// __+--- | |
|
||||
// __--- | OuterL_2 | |
|
||||
// OuterP_3 _-- | | W_1 | W_0
|
||||
// _+ | | |
|
||||
// / \ W_2 | | |
|
||||
// / \ | | InnerL_1 |
|
||||
// | \ | InnerL_2|____-------+ InnerCurve
|
||||
// | \ |____----+ InnerP_0
|
||||
// | . \ __---+ InnerP_1
|
||||
// | \ / InnerP_2
|
||||
// | . /+ InnerP_3
|
||||
// | |
|
||||
// | . |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// OuterP_{n-1} +--------+ InnerP_{n-1}
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// OuterP_n +--------+ InnerP_n
|
||||
//
|
||||
// Returns region with [OuterCurve((OuterT_{2j} + OuterT_{2j-1}) / 2),
|
||||
// OuterCurve((OuterT_{2j} + OuterT_{2j-1}) / 2),
|
||||
// InnerCurve((OuterT_{2j} + OuterT_{2j+1}) / 2),
|
||||
// InnerCurve((OuterT_{2j} + OuterT_{2j+1}) / 2)],
|
||||
// to start and end with half segment.
|
||||
//
|
||||
// _+__----+------+ OuterCurve
|
||||
// _+---- | |######|
|
||||
// __+---#| | |######|
|
||||
// _+---##|####| | |######|
|
||||
// _-- |#####|#####| | |#####|
|
||||
// _+ |#####|#####| | |#####|
|
||||
// / \ |#####|####| | |#####|
|
||||
// / \ |####|#####| | |#####|
|
||||
// | \ |####|####| |____-+-----+ InnerCurve
|
||||
// | \ |####|____+---+
|
||||
// | . \ __+---+
|
||||
// | \ /
|
||||
// | . /+
|
||||
// | |
|
||||
// | . |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// +--------+
|
||||
// | |
|
||||
// | |
|
||||
// +--------+
|
||||
// |########|
|
||||
// |########|
|
||||
// +--------+
|
||||
|
||||
class DashedCornerFinder
|
||||
{
|
||||
typedef mozilla::gfx::Bezier Bezier;
|
||||
typedef mozilla::gfx::Float Float;
|
||||
typedef mozilla::gfx::Point Point;
|
||||
typedef mozilla::gfx::Size Size;
|
||||
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
// Control points for the outer curve and the inner curve.
|
||||
//
|
||||
// outerSectionBezier
|
||||
// |
|
||||
// v _+ 3
|
||||
// ___---#|
|
||||
// 0 +---#######|
|
||||
// |###########|
|
||||
// |###########|
|
||||
// |##########|
|
||||
// |##########|
|
||||
// |#########|
|
||||
// |#####____+ 3
|
||||
// 0 +----
|
||||
// ^
|
||||
// |
|
||||
// innerSectionBezier
|
||||
Bezier outerSectionBezier;
|
||||
Bezier innerSectionBezier;
|
||||
|
||||
Result(const Bezier& aOuterSectionBezier,
|
||||
const Bezier& aInnerSectionBezier)
|
||||
: outerSectionBezier(aOuterSectionBezier),
|
||||
innerSectionBezier(aInnerSectionBezier)
|
||||
{}
|
||||
};
|
||||
|
||||
// aCornerDim.width
|
||||
// |<----------------->|
|
||||
// | |
|
||||
// --+-------------___---+--
|
||||
// ^ | __-- | ^
|
||||
// | | _- | |
|
||||
// | | / | | aBorderWidthH
|
||||
// | | / | |
|
||||
// | | | | v
|
||||
// | | | __--+--
|
||||
// aCornerDim.height | || _-
|
||||
// | || /
|
||||
// | | /
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// v | |
|
||||
// --+---------+
|
||||
// | |
|
||||
// |<------->|
|
||||
// aBorderWidthV
|
||||
DashedCornerFinder(const Bezier& aOuterBezier, const Bezier& aInnerBezier,
|
||||
Float aBorderWidthH, Float aBorderWidthV,
|
||||
const Size& aCornerDim);
|
||||
|
||||
bool HasMore(void) const;
|
||||
Result Next(void);
|
||||
|
||||
private:
|
||||
static const size_t MAX_LOOP = 32;
|
||||
|
||||
// Bezier control points for the outer curve and the inner curve.
|
||||
//
|
||||
// ___---+ outer curve
|
||||
// __-- |
|
||||
// _- |
|
||||
// / |
|
||||
// / |
|
||||
// | |
|
||||
// | __--+ inner curve
|
||||
// | _-
|
||||
// | /
|
||||
// | /
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// +---------+
|
||||
Bezier mOuterBezier;
|
||||
Bezier mInnerBezier;
|
||||
|
||||
Point mLastOuterP;
|
||||
Point mLastInnerP;
|
||||
Float mLastOuterT;
|
||||
Float mLastInnerT;
|
||||
|
||||
// Length for each segment, ratio of the border width at that point.
|
||||
Float mBestDashLength;
|
||||
|
||||
// If one of border-widths is 0, do not calculate mBestDashLength, and draw
|
||||
// segments until it reaches the other side or exceeds mMaxCount.
|
||||
bool mHasZeroBorderWidth;
|
||||
bool mHasMore;
|
||||
|
||||
// The maximum number of segments.
|
||||
size_t mMaxCount;
|
||||
|
||||
enum {
|
||||
// radius.width
|
||||
// |<----------------->|
|
||||
// | |
|
||||
// --+-------------___---+--
|
||||
// ^ | __-- | ^
|
||||
// | | _- | |
|
||||
// | | / + | top-width
|
||||
// | | / | |
|
||||
// | | | | v
|
||||
// | | | __--+--
|
||||
// radius.height | || _-
|
||||
// | || /
|
||||
// | | /
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// v | |
|
||||
// --+----+----+
|
||||
// | |
|
||||
// |<------->|
|
||||
// left-width
|
||||
|
||||
// * top-width == left-width
|
||||
// * radius.width == radius.height
|
||||
// * top-width < radius.width * 2
|
||||
//
|
||||
// Split the perfect circle's arc into 2n segments, each segment's length is
|
||||
// top-width * dashLength. Then split the inner curve and the outer curve
|
||||
// with same angles.
|
||||
//
|
||||
// radius.width
|
||||
// |<---------------------->|
|
||||
// | | v
|
||||
// --+------------------------+--
|
||||
// ^ | | | top-width / 2
|
||||
// | | perfect | |
|
||||
// | | circle's ___---+--
|
||||
// | | arc __-+ | ^
|
||||
// | | | _- | |
|
||||
// radius.height | | | + | +--
|
||||
// | | | / \ | |
|
||||
// | | | | \ | |
|
||||
// | | | | \ | |
|
||||
// | | +->| \ | |
|
||||
// | | +---__ \ | |
|
||||
// | | | --__ \ | |
|
||||
// | | | ---__ \ | |
|
||||
// v | | --_\||
|
||||
// --+----+----+--------------+
|
||||
// | | |
|
||||
// |<-->| |
|
||||
// left-width / 2
|
||||
PERFECT,
|
||||
|
||||
// Other cases.
|
||||
//
|
||||
// Split the outer curve and the inner curve into 2n segments, each segment
|
||||
// satisfies following:
|
||||
// (OuterL_i + InnerL_i) / 2 == dashLength * (W_i + W_{i-1}) / 2
|
||||
OTHER
|
||||
} mType;
|
||||
|
||||
size_t mI;
|
||||
size_t mCount;
|
||||
|
||||
// Determine mType from parameters.
|
||||
void DetermineType(Float aBorderWidthH, Float aBorderWidthV);
|
||||
|
||||
// Reset calculation.
|
||||
void Reset(void);
|
||||
|
||||
// Find next segment.
|
||||
Float FindNext(Float dashLength);
|
||||
|
||||
// Find mBestDashLength for parameters.
|
||||
void FindBestDashLength(Float aMinBorderWidth, Float aMaxBorderWidth,
|
||||
Float aMinBorderRadius, Float aMaxBorderRadius);
|
||||
|
||||
// Fill corner with dashes with given dash length, and return the number of
|
||||
// segments and last segment's dash length.
|
||||
bool GetCountAndLastDashLength(Float aDashLength,
|
||||
size_t* aCount, Float* aActualDashLength);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_DashedCornerFinder_h_ */
|
|
@ -1,553 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
// vim:cindent:ts=2:et:sw=2:
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DottedCornerFinder.h"
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
#include "BorderCache.h"
|
||||
#include "BorderConsts.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
static inline Float
|
||||
Square(Float x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
static Point
|
||||
PointRotateCCW90(const Point& aP)
|
||||
{
|
||||
return Point(aP.y, -aP.x);
|
||||
}
|
||||
|
||||
struct BestOverlap
|
||||
{
|
||||
Float overlap;
|
||||
size_t count;
|
||||
|
||||
BestOverlap()
|
||||
: overlap(0.0f), count(0)
|
||||
{}
|
||||
|
||||
BestOverlap(Float aOverlap, size_t aCount)
|
||||
: overlap(aOverlap), count(aCount)
|
||||
{}
|
||||
};
|
||||
|
||||
static const size_t DottedCornerCacheSize = 256;
|
||||
nsDataHashtable<FourFloatsHashKey, BestOverlap> DottedCornerCache;
|
||||
|
||||
DottedCornerFinder::DottedCornerFinder(const Bezier& aOuterBezier,
|
||||
const Bezier& aInnerBezier,
|
||||
mozilla::css::Corner aCorner,
|
||||
Float aBorderRadiusX,
|
||||
Float aBorderRadiusY,
|
||||
const Point& aC0, Float aR0,
|
||||
const Point& aCn, Float aRn,
|
||||
const Size& aCornerDim)
|
||||
: mOuterBezier(aOuterBezier),
|
||||
mInnerBezier(aInnerBezier),
|
||||
mCorner(aCorner),
|
||||
mNormalSign((aCorner == C_TL || aCorner == C_BR) ? -1.0f : 1.0f),
|
||||
mC0(aC0), mCn(aCn),
|
||||
mR0(aR0), mRn(aRn), mMaxR(std::max(aR0, aRn)),
|
||||
mCurveOrigin(mC0.x, mCn.y),
|
||||
mBestOverlap(0.0f),
|
||||
mHasZeroBorderWidth(false), mHasMore(true),
|
||||
mMaxCount(aCornerDim.width + aCornerDim.height),
|
||||
mType(OTHER),
|
||||
mI(0), mCount(0)
|
||||
{
|
||||
NS_ASSERTION(mR0 > 0.0f || mRn > 0.0f,
|
||||
"At least one side should have non-zero radius.");
|
||||
|
||||
mInnerWidth = fabs(mInnerBezier.mPoints[0].x - mInnerBezier.mPoints[3].x);
|
||||
mInnerHeight = fabs(mInnerBezier.mPoints[0].y - mInnerBezier.mPoints[3].y);
|
||||
|
||||
DetermineType(aBorderRadiusX, aBorderRadiusY);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsSingleCurve(Float aMinR, Float aMaxR,
|
||||
Float aMinBorderRadius, Float aMaxBorderRadius)
|
||||
{
|
||||
return aMinR > 0.0f &&
|
||||
aMinBorderRadius > aMaxR * 4.0f &&
|
||||
aMinBorderRadius / aMaxBorderRadius > 0.5f;
|
||||
}
|
||||
|
||||
void
|
||||
DottedCornerFinder::DetermineType(Float aBorderRadiusX, Float aBorderRadiusY)
|
||||
{
|
||||
// Calculate parameters for the center curve before swap.
|
||||
Float centerCurveWidth = fabs(mC0.x - mCn.x);
|
||||
Float centerCurveHeight = fabs(mC0.y - mCn.y);
|
||||
Point cornerPoint(mCn.x, mC0.y);
|
||||
|
||||
bool swapped = false;
|
||||
if (mR0 < mRn) {
|
||||
// Always draw from wider side to thinner side.
|
||||
Swap(mC0, mCn);
|
||||
Swap(mR0, mRn);
|
||||
Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]);
|
||||
Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]);
|
||||
Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]);
|
||||
Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]);
|
||||
mNormalSign = -mNormalSign;
|
||||
swapped = true;
|
||||
}
|
||||
|
||||
// See the comment at mType declaration for each condition.
|
||||
|
||||
Float minR = std::min(mR0, mRn);
|
||||
Float minBorderRadius = std::min(aBorderRadiusX, aBorderRadiusY);
|
||||
Float maxBorderRadius = std::max(aBorderRadiusX, aBorderRadiusY);
|
||||
if (IsSingleCurve(minR, mMaxR, minBorderRadius, maxBorderRadius)) {
|
||||
if (mR0 == mRn) {
|
||||
Float borderLength;
|
||||
if (minBorderRadius == maxBorderRadius) {
|
||||
mType = PERFECT;
|
||||
borderLength = M_PI * centerCurveHeight / 2.0f;
|
||||
|
||||
mCenterCurveR = centerCurveWidth;
|
||||
} else {
|
||||
mType = SINGLE_CURVE_AND_RADIUS;
|
||||
borderLength = GetQuarterEllipticArcLength(centerCurveWidth,
|
||||
centerCurveHeight);
|
||||
}
|
||||
|
||||
Float diameter = mR0 * 2.0f;
|
||||
size_t count = round(borderLength / diameter);
|
||||
if (count % 2) {
|
||||
count++;
|
||||
}
|
||||
mCount = count / 2 - 1;
|
||||
if (mCount > 0) {
|
||||
mBestOverlap = 1.0f - borderLength / (diameter * count);
|
||||
}
|
||||
} else {
|
||||
mType = SINGLE_CURVE;
|
||||
}
|
||||
}
|
||||
|
||||
if (mType == SINGLE_CURVE_AND_RADIUS || mType == SINGLE_CURVE) {
|
||||
Size cornerSize(centerCurveWidth, centerCurveHeight);
|
||||
GetBezierPointsForCorner(&mCenterBezier, mCorner,
|
||||
cornerPoint, cornerSize);
|
||||
if (swapped) {
|
||||
Swap(mCenterBezier.mPoints[0], mCenterBezier.mPoints[3]);
|
||||
Swap(mCenterBezier.mPoints[1], mCenterBezier.mPoints[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (minR == 0.0f) {
|
||||
mHasZeroBorderWidth = true;
|
||||
}
|
||||
|
||||
if ((mType == SINGLE_CURVE || mType == OTHER) && !mHasZeroBorderWidth) {
|
||||
FindBestOverlap(minR, minBorderRadius, maxBorderRadius);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
DottedCornerFinder::HasMore(void) const
|
||||
{
|
||||
if (mHasZeroBorderWidth) {
|
||||
return mI < mMaxCount && mHasMore;
|
||||
}
|
||||
|
||||
return mI < mCount;
|
||||
}
|
||||
|
||||
DottedCornerFinder::Result
|
||||
DottedCornerFinder::Next(void)
|
||||
{
|
||||
mI++;
|
||||
|
||||
if (mType == PERFECT) {
|
||||
Float phi = mI * 4.0f * mR0 * (1 - mBestOverlap) / mCenterCurveR;
|
||||
if (mCorner == C_TL) {
|
||||
phi = -M_PI / 2.0f - phi;
|
||||
} else if (mCorner == C_TR) {
|
||||
phi = -M_PI / 2.0f + phi;
|
||||
} else if (mCorner == C_BR) {
|
||||
phi = M_PI / 2.0f - phi;
|
||||
} else {
|
||||
phi = M_PI / 2.0f + phi;
|
||||
}
|
||||
|
||||
Point C(mCurveOrigin.x + mCenterCurveR * cos(phi),
|
||||
mCurveOrigin.y + mCenterCurveR * sin(phi));
|
||||
return DottedCornerFinder::Result(C, mR0);
|
||||
}
|
||||
|
||||
// Find unfilled and filled circles.
|
||||
(void)FindNext(mBestOverlap);
|
||||
(void)FindNext(mBestOverlap);
|
||||
return Result(mLastC, mLastR);
|
||||
}
|
||||
|
||||
void
|
||||
DottedCornerFinder::Reset(void)
|
||||
{
|
||||
mLastC = mC0;
|
||||
mLastR = mR0;
|
||||
mLastT = 0.0f;
|
||||
mHasMore = true;
|
||||
}
|
||||
|
||||
void
|
||||
DottedCornerFinder::FindPointAndRadius(Point& C, Float& r,
|
||||
const Point& innerTangent,
|
||||
const Point& normal, Float t)
|
||||
{
|
||||
// Find radius for the given tangent point on the inner curve such that the
|
||||
// circle is also tangent to the outer curve.
|
||||
|
||||
NS_ASSERTION(mType == OTHER, "Wrong mType");
|
||||
|
||||
Float lower = 0.0f;
|
||||
Float upper = mMaxR;
|
||||
const Float DIST_MARGIN = 0.1f;
|
||||
for (size_t i = 0; i < MAX_LOOP; i++) {
|
||||
r = (upper + lower) / 2.0f;
|
||||
C = innerTangent + normal * r;
|
||||
|
||||
Point Near = FindBezierNearestPoint(mOuterBezier, C, t);
|
||||
Float distSquare = (C - Near).LengthSquare();
|
||||
|
||||
if (distSquare > Square(r + DIST_MARGIN)) {
|
||||
lower = r;
|
||||
} else if (distSquare < Square(r - DIST_MARGIN)) {
|
||||
upper = r;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Float
|
||||
DottedCornerFinder::FindNext(Float overlap)
|
||||
{
|
||||
Float lower = mLastT;
|
||||
Float upper = 1.0f;
|
||||
Float t;
|
||||
|
||||
Point C = mLastC;
|
||||
Float r = 0.0f;
|
||||
|
||||
Float factor = (1.0f - overlap);
|
||||
|
||||
Float circlesDist = 0.0f;
|
||||
Float expectedDist = 0.0f;
|
||||
|
||||
const Float DIST_MARGIN = 0.1f;
|
||||
if (mType == SINGLE_CURVE_AND_RADIUS) {
|
||||
r = mR0;
|
||||
|
||||
expectedDist = (r + mLastR) * factor;
|
||||
|
||||
// Find C_i on the center curve.
|
||||
for (size_t i = 0; i < MAX_LOOP; i++) {
|
||||
t = (upper + lower) / 2.0f;
|
||||
C = GetBezierPoint(mCenterBezier, t);
|
||||
|
||||
// Check overlap along arc.
|
||||
circlesDist = GetBezierLength(mCenterBezier, mLastT, t);
|
||||
if (circlesDist < expectedDist - DIST_MARGIN) {
|
||||
lower = t;
|
||||
} else if (circlesDist > expectedDist + DIST_MARGIN) {
|
||||
upper = t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (mType == SINGLE_CURVE) {
|
||||
// Find C_i on the center curve, and calculate r_i.
|
||||
for (size_t i = 0; i < MAX_LOOP; i++) {
|
||||
t = (upper + lower) / 2.0f;
|
||||
C = GetBezierPoint(mCenterBezier, t);
|
||||
|
||||
Point Diff = GetBezierDifferential(mCenterBezier, t);
|
||||
Float DiffLength = Diff.Length();
|
||||
if (DiffLength == 0.0f) {
|
||||
// Basically this shouldn't happen.
|
||||
// If differential is 0, we cannot calculate tangent circle,
|
||||
// skip this point.
|
||||
t = (t + upper) / 2.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
Point normal = PointRotateCCW90(Diff / DiffLength) * (-mNormalSign);
|
||||
r = CalculateDistanceToEllipticArc(C, normal, mCurveOrigin,
|
||||
mInnerWidth, mInnerHeight);
|
||||
|
||||
// Check overlap along arc.
|
||||
circlesDist = GetBezierLength(mCenterBezier, mLastT, t);
|
||||
expectedDist = (r + mLastR) * factor;
|
||||
if (circlesDist < expectedDist - DIST_MARGIN) {
|
||||
lower = t;
|
||||
} else if (circlesDist > expectedDist + DIST_MARGIN) {
|
||||
upper = t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Float distSquareMax = Square(mMaxR * 3.0f);
|
||||
Float circlesDistSquare = 0.0f;
|
||||
|
||||
// Find C_i and r_i.
|
||||
for (size_t i = 0; i < MAX_LOOP; i++) {
|
||||
t = (upper + lower) / 2.0f;
|
||||
Point innerTangent = GetBezierPoint(mInnerBezier, t);
|
||||
if ((innerTangent - mLastC).LengthSquare() > distSquareMax) {
|
||||
// It's clear that this tangent point is too far, skip it.
|
||||
upper = t;
|
||||
continue;
|
||||
}
|
||||
|
||||
Point Diff = GetBezierDifferential(mInnerBezier, t);
|
||||
Float DiffLength = Diff.Length();
|
||||
if (DiffLength == 0.0f) {
|
||||
// Basically this shouldn't happen.
|
||||
// If differential is 0, we cannot calculate tangent circle,
|
||||
// skip this point.
|
||||
t = (t + upper) / 2.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
Point normal = PointRotateCCW90(Diff / DiffLength) * mNormalSign;
|
||||
FindPointAndRadius(C, r, innerTangent, normal, t);
|
||||
|
||||
// Check overlap with direct distance.
|
||||
circlesDistSquare = (C - mLastC).LengthSquare();
|
||||
expectedDist = (r + mLastR) * factor;
|
||||
if (circlesDistSquare < Square(expectedDist - DIST_MARGIN)) {
|
||||
lower = t;
|
||||
} else if (circlesDistSquare > Square(expectedDist + DIST_MARGIN)) {
|
||||
upper = t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
circlesDist = sqrt(circlesDistSquare);
|
||||
}
|
||||
|
||||
mLastT = t;
|
||||
mLastC = C;
|
||||
mLastR = r;
|
||||
|
||||
if (mHasZeroBorderWidth) {
|
||||
const Float T_MARGIN = 0.001f;
|
||||
if (mLastT >= 1.0f - T_MARGIN ||
|
||||
(mLastC - mCn).LengthSquare() < Square(mLastR)) {
|
||||
mHasMore = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (expectedDist == 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f - circlesDist * factor / expectedDist;
|
||||
}
|
||||
|
||||
void
|
||||
DottedCornerFinder::FindBestOverlap(Float aMinR, Float aMinBorderRadius,
|
||||
Float aMaxBorderRadius)
|
||||
{
|
||||
// If overlap is not calculateable, find it with binary search,
|
||||
// such that there exists i that C_i == C_n with the given overlap.
|
||||
|
||||
FourFloats key(aMinR, mMaxR,
|
||||
aMinBorderRadius, aMaxBorderRadius);
|
||||
BestOverlap best;
|
||||
if (DottedCornerCache.Get(key, &best)) {
|
||||
mCount = best.count;
|
||||
mBestOverlap = best.overlap;
|
||||
return;
|
||||
}
|
||||
|
||||
Float lower = 0.0f;
|
||||
Float upper = 0.5f;
|
||||
// Start from lower bound to find the minimum number of circles.
|
||||
Float overlap = 0.0f;
|
||||
mBestOverlap = overlap;
|
||||
size_t targetCount = 0;
|
||||
|
||||
const Float OVERLAP_MARGIN = 0.1f;
|
||||
for (size_t j = 0; j < MAX_LOOP; j++) {
|
||||
Reset();
|
||||
|
||||
size_t count;
|
||||
Float actualOverlap;
|
||||
if (!GetCountAndLastOverlap(overlap, &count, &actualOverlap)) {
|
||||
if (j == 0) {
|
||||
mCount = mMaxCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
if (count < 3 || (count == 3 && actualOverlap > 0.5f)) {
|
||||
// |count == 3 && actualOverlap > 0.5f| means there could be
|
||||
// a circle but it is too near from both ends.
|
||||
//
|
||||
// if actualOverlap == 0.0
|
||||
// 1 2 3
|
||||
// +-------+-------+-------+-------+
|
||||
// | ##### | ***** | ##### | ##### |
|
||||
// |#######|*******|#######|#######|
|
||||
// |###+###|***+***|###+###|###+###|
|
||||
// |# C_0 #|* C_1 *|# C_2 #|# C_n #|
|
||||
// | ##### | ***** | ##### | ##### |
|
||||
// +-------+-------+-------+-------+
|
||||
// |
|
||||
// V
|
||||
// +-------+---+-------+---+-------+
|
||||
// | ##### | | ##### | | ##### |
|
||||
// |#######| |#######| |#######|
|
||||
// |###+###| |###+###| |###+###| Find the best overlap to place
|
||||
// |# C_0 #| |# C_1 #| |# C_n #| C_1 at the middle of them
|
||||
// | ##### | | ##### | | ##### |
|
||||
// +-------+---+-------+---|-------+
|
||||
//
|
||||
// if actualOverlap == 0.5
|
||||
// 1 2 3
|
||||
// +-------+-------+-------+---+
|
||||
// | ##### | ***** | ##### |## |
|
||||
// |#######|*******|##### C_n #|
|
||||
// |###+###|***+***|###+###+###|
|
||||
// |# C_0 #|* C_1 *|# C_2 #|###|
|
||||
// | ##### | ***** | ##### |## |
|
||||
// +-------+-------+-------+---+
|
||||
// |
|
||||
// V
|
||||
// +-------+-+-------+-+-------+
|
||||
// | ##### | | ##### | | ##### |
|
||||
// |#######| |#######| |#######|
|
||||
// |###+###| |###+###| |###+###| Even if we place C_1 at the middle
|
||||
// |# C_0 #| |# C_1 #| |# C_n #| of them, it's too near from them
|
||||
// | ##### | | ##### | | ##### |
|
||||
// +-------+-+-------+-|-------+
|
||||
// |
|
||||
// V
|
||||
// +-------+-----------+-------+
|
||||
// | ##### | | ##### |
|
||||
// |#######| |#######|
|
||||
// |###+###| |###+###| Do not draw any circle
|
||||
// |# C_0 #| |# C_n #|
|
||||
// | ##### | | ##### |
|
||||
// +-------+-----------+-------+
|
||||
mCount = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// targetCount should be 2n, as we're searching C_1 to C_n.
|
||||
//
|
||||
// targetCount = 4
|
||||
// mCount = 1
|
||||
// 1 2 3 4
|
||||
// +-------+-------+-------+-------+-------+
|
||||
// | ##### | ***** | ##### | ***** | ##### |
|
||||
// |#######|*******|#######|*******|#######|
|
||||
// |###+###|***+***|###+###|***+***|###+###|
|
||||
// |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_n #|
|
||||
// | ##### | ***** | ##### | ***** | ##### |
|
||||
// +-------+-------+-------+-------+-------+
|
||||
// 1
|
||||
//
|
||||
// targetCount = 6
|
||||
// mCount = 2
|
||||
// 1 2 3 4 5 6
|
||||
// +-------+-------+-------+-------+-------+-------+-------+
|
||||
// | ##### | ***** | ##### | ***** | ##### | ***** | ##### |
|
||||
// |#######|*******|#######|*******|#######|*******|#######|
|
||||
// |###+###|***+***|###+###|***+***|###+###|***+***|###+###|
|
||||
// |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_4 #|* C_5 *|# C_n #|
|
||||
// | ##### | ***** | ##### | ***** | ##### | ***** | ##### |
|
||||
// +-------+-------+-------+-------+-------+-------+-------+
|
||||
// 1 2
|
||||
if (count % 2) {
|
||||
targetCount = count + 1;
|
||||
} else {
|
||||
targetCount = count;
|
||||
}
|
||||
|
||||
mCount = targetCount / 2 - 1;
|
||||
}
|
||||
|
||||
if (count == targetCount) {
|
||||
mBestOverlap = overlap;
|
||||
|
||||
if (fabs(actualOverlap - overlap) < OVERLAP_MARGIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We started from upper bound, no need to update range when j == 0.
|
||||
if (j > 0) {
|
||||
if (actualOverlap > overlap) {
|
||||
lower = overlap;
|
||||
} else {
|
||||
upper = overlap;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// |j == 0 && count != targetCount| means that |targetCount = count + 1|,
|
||||
// and we started from upper bound, no need to update range when j == 0.
|
||||
if (j > 0) {
|
||||
if (count > targetCount) {
|
||||
upper = overlap;
|
||||
} else {
|
||||
lower = overlap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
overlap = (upper + lower) / 2.0f;
|
||||
}
|
||||
|
||||
if (DottedCornerCache.Count() > DottedCornerCacheSize) {
|
||||
DottedCornerCache.Clear();
|
||||
}
|
||||
DottedCornerCache.Put(key, BestOverlap(mBestOverlap, mCount));
|
||||
}
|
||||
|
||||
bool
|
||||
DottedCornerFinder::GetCountAndLastOverlap(Float aOverlap,
|
||||
size_t* aCount,
|
||||
Float* aActualOverlap)
|
||||
{
|
||||
// Return the number of circles and the last circles' overlap for the
|
||||
// given overlap.
|
||||
|
||||
Reset();
|
||||
|
||||
const Float T_MARGIN = 0.001f;
|
||||
const Float DIST_MARGIN = 0.1f;
|
||||
const Float DIST_MARGIN_SQUARE = Square(DIST_MARGIN);
|
||||
for (size_t i = 0; i < mMaxCount; i++) {
|
||||
Float actualOverlap = FindNext(aOverlap);
|
||||
if (mLastT >= 1.0f - T_MARGIN ||
|
||||
(mLastC - mCn).LengthSquare() < DIST_MARGIN_SQUARE) {
|
||||
*aCount = i + 1;
|
||||
*aActualOverlap = actualOverlap;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,414 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_DottedCornerFinder_h_
|
||||
#define mozilla_DottedCornerFinder_h_
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/BezierUtils.h"
|
||||
#include "gfxRect.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Calculate C_i and r_i for each filled/unfilled circles in dotted corner.
|
||||
// Returns circle with C_{2j} and r_{2j} where 0 < 2j < n.
|
||||
//
|
||||
// ____-----------+
|
||||
// __---- ***** ###|
|
||||
// __---- ********* ####|
|
||||
// __--- ##### ***********#####|
|
||||
// _-- ######### *****+*****#####+ C_0
|
||||
// _- ########### *** C_1****#####|
|
||||
// / #####+##### ********* ####|
|
||||
// / . ### C_2 ### ***** ###|
|
||||
// | ######### ____-------+
|
||||
// | . #####____-----
|
||||
// | __----
|
||||
// | . /
|
||||
// | /
|
||||
// | ***** |
|
||||
// | ******* |
|
||||
// |*********|
|
||||
// |****+****|
|
||||
// | C_{n-1} |
|
||||
// | ******* |
|
||||
// | ***** |
|
||||
// | ##### |
|
||||
// | ####### |
|
||||
// |#########|
|
||||
// +----+----+
|
||||
// C_n
|
||||
|
||||
class DottedCornerFinder
|
||||
{
|
||||
typedef mozilla::gfx::Bezier Bezier;
|
||||
typedef mozilla::gfx::Float Float;
|
||||
typedef mozilla::gfx::Point Point;
|
||||
typedef mozilla::gfx::Size Size;
|
||||
|
||||
public:
|
||||
struct Result
|
||||
{
|
||||
// Center point of dot and its radius.
|
||||
Point C;
|
||||
Float r;
|
||||
|
||||
Result(const Point& aC, Float aR)
|
||||
: C(aC), r(aR)
|
||||
{}
|
||||
};
|
||||
|
||||
// aBorderRadiusX
|
||||
// aCornerDim.width
|
||||
// |<----------------->|
|
||||
// | | v
|
||||
// --+-------------___---+--
|
||||
// ^ | __-- | |
|
||||
// | | _- | | aR0
|
||||
// | | / aC0 +--
|
||||
// | | / | ^
|
||||
// | | | |
|
||||
// aBorderRadiusY | | | __--+
|
||||
// aCornerDim.height | || _-
|
||||
// | || /
|
||||
// | | /
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// v | aCn |
|
||||
// --+----+----+
|
||||
// | |
|
||||
// |<-->|
|
||||
// aRn
|
||||
//
|
||||
// aCornerDim and (aBorderRadiusX, aBorderRadiusY) can be different when
|
||||
// aBorderRadiusX is smaller than aRn*2 or
|
||||
// aBorderRadiusY is smaller than aR0*2.
|
||||
//
|
||||
// aCornerDim.width
|
||||
// |<----------------->|
|
||||
// | |
|
||||
// | aBorderRadiusX |
|
||||
// |<--------->| |
|
||||
// | | |
|
||||
// -------------------+-------__--+-------+--
|
||||
// ^ ^ | _- | ^
|
||||
// | | | / | |
|
||||
// | | | / | |
|
||||
// | aBorderRadiusY | | | | | aR0
|
||||
// | | || | |
|
||||
// | | | | |
|
||||
// aCornerDim.height | v | | v
|
||||
// | --+ aC0 +--
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// v | aCn |
|
||||
// -------------------+---------+---------+
|
||||
// | |
|
||||
// |<------->|
|
||||
// aRn
|
||||
DottedCornerFinder(const Bezier& aOuterBezier, const Bezier& aInnerBezier,
|
||||
mozilla::css::Corner aCorner,
|
||||
Float aBorderRadiusX, Float aBorderRadiusY,
|
||||
const Point& aC0, Float aR0, const Point& aCn, Float aRn,
|
||||
const Size& aCornerDim);
|
||||
|
||||
bool HasMore(void) const;
|
||||
Result Next(void);
|
||||
|
||||
private:
|
||||
static const size_t MAX_LOOP = 32;
|
||||
|
||||
// Bezier control points for the outer curve, the inner curve, and a curve
|
||||
// that center points of circles are on (center curve).
|
||||
//
|
||||
// ___---+ outer curve
|
||||
// __-- |
|
||||
// _- |
|
||||
// / __---+ center curve
|
||||
// / __-- |
|
||||
// | / |
|
||||
// | / __--+ inner curve
|
||||
// | | _-
|
||||
// | | /
|
||||
// | | /
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// +----+----+
|
||||
Bezier mOuterBezier;
|
||||
Bezier mInnerBezier;
|
||||
Bezier mCenterBezier;
|
||||
|
||||
mozilla::css::Corner mCorner;
|
||||
|
||||
// Sign of the normal vector used in radius calculation, flipped depends on
|
||||
// corner and start and end radii.
|
||||
Float mNormalSign;
|
||||
|
||||
// Center points and raii for start and end circles, mR0 >= mRn.
|
||||
// mMaxR = max(mR0, mRn)
|
||||
//
|
||||
// v
|
||||
// ___---+------
|
||||
// __-- #|# | mRn
|
||||
// _- ##|## |
|
||||
// / ##+## ---
|
||||
// / mCn ^
|
||||
// | #|#
|
||||
// | __--+
|
||||
// | _-
|
||||
// | /
|
||||
// | /
|
||||
// | |
|
||||
// | |
|
||||
// | ##### |
|
||||
// | ####### |
|
||||
// |#########|
|
||||
// +----+----+
|
||||
// |## mC0 ##|
|
||||
// | ####### |
|
||||
// | ##### |
|
||||
// | |
|
||||
// |<-->|
|
||||
//
|
||||
// mR0
|
||||
//
|
||||
Point mC0;
|
||||
Point mCn;
|
||||
Float mR0;
|
||||
Float mRn;
|
||||
Float mMaxR;
|
||||
|
||||
// Parameters for the center curve with perfect circle and the inner curve.
|
||||
//
|
||||
// ___---+
|
||||
// __-- |
|
||||
// _- |
|
||||
// / __---+
|
||||
// / __-- |
|
||||
// | / |
|
||||
// | / __--+--
|
||||
// | | _- | ^
|
||||
// | | / | |
|
||||
// | | / | |
|
||||
// | | | | |
|
||||
// | | | | | mInnerHeight
|
||||
// | | | | |
|
||||
// | | | | |
|
||||
// | | | | v
|
||||
// +----+----+---------+
|
||||
// | | | mCurveOrigin
|
||||
// | |<------->|
|
||||
// | mInnerWidth |
|
||||
// | |
|
||||
// |<------------>|
|
||||
// mCenterCurveR
|
||||
//
|
||||
Point mCurveOrigin;
|
||||
Float mCenterCurveR;
|
||||
Float mInnerWidth;
|
||||
Float mInnerHeight;
|
||||
|
||||
Point mLastC;
|
||||
Float mLastR;
|
||||
Float mLastT;
|
||||
|
||||
// Overlap between two circles.
|
||||
// It uses arc length on PERFECT, SINGLE_CURVE_AND_RADIUS, and SINGLE_CURVE,
|
||||
// and direct distance on OTHER.
|
||||
Float mBestOverlap;
|
||||
|
||||
// If one of border-widths is 0, do not calculate overlap, and draw circles
|
||||
// until it reaches the other side or exceeds mMaxCount.
|
||||
bool mHasZeroBorderWidth;
|
||||
bool mHasMore;
|
||||
|
||||
// The maximum number of filled/unfilled circles.
|
||||
size_t mMaxCount;
|
||||
|
||||
enum {
|
||||
// radius.width
|
||||
// |<----------------->|
|
||||
// | |
|
||||
// --+-------------___---+----
|
||||
// ^ | __-- #|# ^
|
||||
// | | _- ##|## |
|
||||
// | | / ##+## | top-width
|
||||
// | | / ##|## |
|
||||
// | | | #|# v
|
||||
// | | | __--+----
|
||||
// radius.height | || _-
|
||||
// | || /
|
||||
// | | /
|
||||
// | | |
|
||||
// | | |
|
||||
// | | ##### |
|
||||
// | | ####### |
|
||||
// v |#########|
|
||||
// --+----+----+
|
||||
// |#########|
|
||||
// | ####### |
|
||||
// | ##### |
|
||||
// | |
|
||||
// |<------->|
|
||||
// left-width
|
||||
|
||||
// * top-width == left-width
|
||||
// * radius.width == radius.height
|
||||
// * top-width < radius.width * 2
|
||||
//
|
||||
// All circles has same radii and are on single perfect circle's arc.
|
||||
// Overlap is known.
|
||||
//
|
||||
// Split the perfect circle's arc into 2n segments, each segment's length is
|
||||
// top-width * (1 - overlap). Place each circle's center point C_i on each
|
||||
// end of the segment, each circle's radius r_i is top-width / 2
|
||||
//
|
||||
// #####
|
||||
// #######
|
||||
// perfect #########
|
||||
// circle's ___---+####
|
||||
// arc ##### __-- ## C_0 ##
|
||||
// | #####_- ###|###
|
||||
// | ####+#### ##|##
|
||||
// | ##/C_i ## |
|
||||
// | |###### |
|
||||
// | | ##### |
|
||||
// +->| |
|
||||
// | |
|
||||
// ##|## |
|
||||
// ###|### |
|
||||
// ####|#### |
|
||||
// ####+-------------------+
|
||||
// ## C_n ##
|
||||
// #######
|
||||
// #####
|
||||
PERFECT,
|
||||
|
||||
// * top-width == left-width
|
||||
// * 0.5 < radius.width / radius.height < 2.0
|
||||
// * top-width < min(radius.width, radius.height) * 2
|
||||
//
|
||||
// All circles has same radii and are on single elliptic arc.
|
||||
// Overlap is known.
|
||||
//
|
||||
// Split the elliptic arc into 2n segments, each segment's length is
|
||||
// top-width * (1 - overlap). Place each circle's center point C_i on each
|
||||
// end of the segment, each circle's radius r_i is top-width / 2
|
||||
//
|
||||
// #####
|
||||
// #######
|
||||
// ##### #########
|
||||
// ####### ____----+####
|
||||
// elliptic ######__--- ## C_0 ##
|
||||
// arc ##__+-### ###|###
|
||||
// | / # C_i # ##|##
|
||||
// +--> / ##### |
|
||||
// | |
|
||||
// ###|# |
|
||||
// ###|### |
|
||||
// ####|#### |
|
||||
// ####+------------------------+
|
||||
// ## C_n ##
|
||||
// #######
|
||||
// #####
|
||||
SINGLE_CURVE_AND_RADIUS,
|
||||
|
||||
// * top-width != left-width
|
||||
// * 0 < min(top-width, left-width)
|
||||
// * 0.5 < radius.width / radius.height < 2.0
|
||||
// * max(top-width, left-width) < min(radius.width, radius.height) * 2
|
||||
//
|
||||
// All circles are on single elliptic arc.
|
||||
// Overlap is unknown.
|
||||
//
|
||||
// Place each circle's center point C_i on elliptic arc, each circle's
|
||||
// radius r_i is the distance between the center point and the inner curve.
|
||||
// The arc segment's length between C_i and C_{i-1} is
|
||||
// (r_i + r_{i-1}) * (1 - overlap).
|
||||
//
|
||||
// outer curve
|
||||
// /
|
||||
// /
|
||||
// / / center curve
|
||||
// / ####### /
|
||||
// /## /#
|
||||
// +# / #
|
||||
// /# / #
|
||||
// / # C_i / #
|
||||
// / # + # /
|
||||
// / # / \ # / inner curve
|
||||
// # / \ #/
|
||||
// # / r_i \+
|
||||
// #/ ##/
|
||||
// / ####### /
|
||||
// /
|
||||
SINGLE_CURVE,
|
||||
|
||||
// Other cases.
|
||||
// Circles are not on single elliptic arc.
|
||||
// Overlap are unknown.
|
||||
//
|
||||
// Place tangent point innerTangent on the inner curve and find circle's
|
||||
// center point C_i and radius r_i where the circle is also tangent to the
|
||||
// outer curve.
|
||||
// Distance between C_i and C_{i-1} is (r_i + r_{i-1}) * (1 - overlap).
|
||||
//
|
||||
// outer curve
|
||||
// /
|
||||
// /
|
||||
// /
|
||||
// / #######
|
||||
// /## ##
|
||||
// +# #
|
||||
// /# \ #
|
||||
// / # \ #
|
||||
// / # + # /
|
||||
// / # C_i \ # / inner curve
|
||||
// # \ #/
|
||||
// # r_i \+
|
||||
// ## ##/ innerTangent
|
||||
// ####### /
|
||||
// /
|
||||
OTHER
|
||||
} mType;
|
||||
|
||||
size_t mI;
|
||||
size_t mCount;
|
||||
|
||||
// Determine mType from parameters.
|
||||
void DetermineType(Float aBorderRadiusX, Float aBorderRadiusY);
|
||||
|
||||
// Reset calculation.
|
||||
void Reset(void);
|
||||
|
||||
// Find radius for the given tangent point on the inner curve such that the
|
||||
// circle is also tangent to the outer curve.
|
||||
void FindPointAndRadius(Point& C, Float& r, const Point& innerTangent,
|
||||
const Point& normal, Float t);
|
||||
|
||||
// Find next dot.
|
||||
Float FindNext(Float overlap);
|
||||
|
||||
// Find mBestOverlap for parameters.
|
||||
void FindBestOverlap(Float aMinR,
|
||||
Float aMinBorderRadius, Float aMaxBorderRadius);
|
||||
|
||||
// Fill corner with dots with given overlap, and return the number of dots
|
||||
// and last two dots's overlap.
|
||||
bool GetCountAndLastOverlap(Float aOverlap,
|
||||
size_t* aCount, Float* aActualOverlap);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_DottedCornerFinder_h_ */
|
|
@ -11,7 +11,6 @@
|
|||
#include "mozilla/RestyleManager.h"
|
||||
|
||||
#include <algorithm> // For std::max
|
||||
#include "mozilla/EffectSet.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "AnimationCommon.h" // For GetLayerAnimationInfo
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
using EventTarget = ::mozilla::dom::EventTarget;
|
||||
|
||||
nsRefPtrHashtable<nsUint32HashKey, dom::Touch>* TouchManager::gCaptureTouchList;
|
||||
|
||||
/*static*/ void
|
||||
|
|
|
@ -118,11 +118,9 @@ UNIFIED_SOURCES += [
|
|||
'AccessibleCaretEventHub.cpp',
|
||||
'AccessibleCaretManager.cpp',
|
||||
'ActiveLayerTracker.cpp',
|
||||
'DashedCornerFinder.cpp',
|
||||
'DisplayItemClip.cpp',
|
||||
'DisplayItemScrollClip.cpp',
|
||||
'DisplayListClipState.cpp',
|
||||
'DottedCornerFinder.cpp',
|
||||
'FrameLayerBuilder.cpp',
|
||||
'FramePropertyTable.cpp',
|
||||
'GeometryUtils.cpp',
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include "mozilla/gfx/Helpers.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "BorderConsts.h"
|
||||
#include "DashedCornerFinder.h"
|
||||
#include "DottedCornerFinder.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsCSSColorUtils.h"
|
||||
|
@ -63,10 +61,6 @@ static void ComputeBorderCornerDimensions(const Rect& aOuterRect,
|
|||
#define NEXT_SIDE(_s) mozilla::css::Side(((_s) + 1) & 3)
|
||||
#define PREV_SIDE(_s) mozilla::css::Side(((_s) + 3) & 3)
|
||||
|
||||
// given a corner index, get the previous and next corner index
|
||||
#define NEXT_CORNER(_s) mozilla::css::Corner(((_s) + 1) & 3)
|
||||
#define PREV_CORNER(_s) mozilla::css::Corner(((_s) + 3) & 3)
|
||||
|
||||
// from the given base color and the background color, turn
|
||||
// color into a color for the given border pattern style
|
||||
static Color MakeBorderColor(nscolor aColor,
|
||||
|
@ -329,6 +323,8 @@ bool
|
|||
nsCSSBorderRenderer::IsSolidCornerStyle(uint8_t aStyle, mozilla::css::Corner aCorner)
|
||||
{
|
||||
switch (aStyle) {
|
||||
case NS_STYLE_BORDER_STYLE_DOTTED:
|
||||
case NS_STYLE_BORDER_STYLE_DASHED:
|
||||
case NS_STYLE_BORDER_STYLE_SOLID:
|
||||
return true;
|
||||
|
||||
|
@ -404,6 +400,8 @@ nsCSSBorderRenderer::BorderColorStyleForSolidCorner(uint8_t aStyle, mozilla::css
|
|||
// note that this function assumes that the corner is already solid,
|
||||
// as per the earlier function
|
||||
switch (aStyle) {
|
||||
case NS_STYLE_BORDER_STYLE_DOTTED:
|
||||
case NS_STYLE_BORDER_STYLE_DASHED:
|
||||
case NS_STYLE_BORDER_STYLE_SOLID:
|
||||
case NS_STYLE_BORDER_STYLE_DOUBLE:
|
||||
return BorderColorStyleSolid;
|
||||
|
@ -1055,45 +1053,6 @@ nsCSSBorderRenderer::GetStraightBorderPoint(mozilla::css::Side aSide,
|
|||
return P;
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSBorderRenderer::GetOuterAndInnerBezier(Bezier* aOuterBezier,
|
||||
Bezier* aInnerBezier,
|
||||
mozilla::css::Corner aCorner)
|
||||
{
|
||||
// Return bezier control points for outer and inner curve for given corner.
|
||||
//
|
||||
// ___---+ outer curve
|
||||
// __-- |
|
||||
// _- |
|
||||
// / |
|
||||
// / |
|
||||
// | |
|
||||
// | __--+ inner curve
|
||||
// | _-
|
||||
// | /
|
||||
// | /
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
// +---------+
|
||||
|
||||
mozilla::css::Side sideH(GetHorizontalSide(aCorner));
|
||||
mozilla::css::Side sideV(GetVerticalSide(aCorner));
|
||||
|
||||
Size innerCornerSize(std::max(0.0f, mBorderRadii[aCorner].width -
|
||||
mBorderWidths[sideV]),
|
||||
std::max(0.0f, mBorderRadii[aCorner].height -
|
||||
mBorderWidths[sideH]));
|
||||
|
||||
GetBezierPointsForCorner(aOuterBezier, aCorner, mOuterRect.AtCorner(aCorner),
|
||||
mBorderRadii[aCorner]);
|
||||
|
||||
GetBezierPointsForCorner(aInnerBezier, aCorner, mInnerRect.AtCorner(aCorner),
|
||||
innerCornerSize);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSBorderRenderer::FillSolidBorder(const Rect& aOuterRect,
|
||||
const Rect& aInnerRect,
|
||||
|
@ -1333,35 +1292,6 @@ nsCSSBorderRenderer::DrawBorderSides(int aSides)
|
|||
borderRenderStyle == NS_STYLE_BORDER_STYLE_HIDDEN)
|
||||
return;
|
||||
|
||||
if (borderRenderStyle == NS_STYLE_BORDER_STYLE_DASHED ||
|
||||
borderRenderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
|
||||
// Draw each corner separately, with the given side's color.
|
||||
if (aSides & SIDE_BIT_TOP) {
|
||||
DrawDashedOrDottedCorner(NS_SIDE_TOP, C_TL);
|
||||
} else if (aSides & SIDE_BIT_LEFT) {
|
||||
DrawDashedOrDottedCorner(NS_SIDE_LEFT, C_TL);
|
||||
}
|
||||
|
||||
if (aSides & SIDE_BIT_TOP) {
|
||||
DrawDashedOrDottedCorner(NS_SIDE_TOP, C_TR);
|
||||
} else if (aSides & SIDE_BIT_RIGHT) {
|
||||
DrawDashedOrDottedCorner(NS_SIDE_RIGHT, C_TR);
|
||||
}
|
||||
|
||||
if (aSides & SIDE_BIT_BOTTOM) {
|
||||
DrawDashedOrDottedCorner(NS_SIDE_BOTTOM, C_BL);
|
||||
} else if (aSides & SIDE_BIT_LEFT) {
|
||||
DrawDashedOrDottedCorner(NS_SIDE_LEFT, C_BL);
|
||||
}
|
||||
|
||||
if (aSides & SIDE_BIT_BOTTOM) {
|
||||
DrawDashedOrDottedCorner(NS_SIDE_BOTTOM, C_BR);
|
||||
} else if (aSides & SIDE_BIT_RIGHT) {
|
||||
DrawDashedOrDottedCorner(NS_SIDE_RIGHT, C_BR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// -moz-border-colors is a hack; if we have it for a border, then
|
||||
// it's always drawn solid, and each color is given 1px. The last
|
||||
// color is used for the remainder of the border's size. Just
|
||||
|
@ -1386,6 +1316,8 @@ nsCSSBorderRenderer::DrawBorderSides(int aSides)
|
|||
|
||||
switch (borderRenderStyle) {
|
||||
case NS_STYLE_BORDER_STYLE_SOLID:
|
||||
case NS_STYLE_BORDER_STYLE_DASHED:
|
||||
case NS_STYLE_BORDER_STYLE_DOTTED:
|
||||
borderColorStyleTopLeft[0] = BorderColorStyleSolid;
|
||||
|
||||
borderColorStyleBottomRight[0] = BorderColorStyleSolid;
|
||||
|
@ -1607,7 +1539,7 @@ void
|
|||
nsCSSBorderRenderer::SetupDashedOptions(StrokeOptions* aStrokeOptions,
|
||||
Float aDash[2],
|
||||
mozilla::css::Side aSide,
|
||||
Float aBorderLength, bool isCorner)
|
||||
Float aBorderLength)
|
||||
{
|
||||
uint8_t style = mBorderStyles[aSide];
|
||||
Float borderWidth = mBorderWidths[aSide];
|
||||
|
@ -1755,23 +1687,6 @@ nsCSSBorderRenderer::SetupDashedOptions(StrokeOptions* aStrokeOptions,
|
|||
// Draw half segments on both ends.
|
||||
aStrokeOptions->mDashOffset = halfDash;
|
||||
}
|
||||
} else if (isCorner) {
|
||||
// If side ends with filled full segment, corner should start with unfilled
|
||||
// full segment.
|
||||
//
|
||||
// corner side
|
||||
// ------------>|<---------------------------
|
||||
// |
|
||||
// __+---+---+---+---+---+---+---+---+
|
||||
// _+- | |###|###| | |###|###| |
|
||||
// /##| | |###|###| | |###|###| |
|
||||
// +####| | |###|###| | |###|###| |
|
||||
// /#\####| _+--+---+---+---+---+---+---+---+
|
||||
// |####\##+-
|
||||
// |#####+-
|
||||
// +--###/
|
||||
// | --+
|
||||
aStrokeOptions->mDashOffset = fullDash;
|
||||
}
|
||||
|
||||
aStrokeOptions->mDashPattern = aDash;
|
||||
|
@ -1859,7 +1774,7 @@ nsCSSBorderRenderer::DrawDashedOrDottedSide(mozilla::css::Side aSide)
|
|||
|
||||
StrokeOptions strokeOptions(borderWidth);
|
||||
Float dash[2];
|
||||
SetupDashedOptions(&strokeOptions, dash, aSide, borderLength, false);
|
||||
SetupDashedOptions(&strokeOptions, dash, aSide, borderLength);
|
||||
|
||||
mDrawTarget->StrokeLine(start, end,
|
||||
ColorPattern(ToDeviceColor(borderColor)),
|
||||
|
@ -2116,241 +2031,6 @@ nsCSSBorderRenderer::DrawDottedSideSlow(mozilla::css::Side aSide)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSBorderRenderer::DrawDashedOrDottedCorner(mozilla::css::Side aSide,
|
||||
mozilla::css::Corner aCorner)
|
||||
{
|
||||
// Draw dashed/dotted corner with following approach.
|
||||
//
|
||||
// dashed corner
|
||||
// If both side has same border-width and border-width <= 2.0, draw dashed
|
||||
// line along the corner, with appropriate dash length and gap to make the
|
||||
// corner symmetric as far as possible. Dash length equals to the gap, and
|
||||
// the ratio of the dash length to border-width is the maximum value in in
|
||||
// [1, 3] range.
|
||||
// Otherwise, draw dashed segments along the corner, keeping same dash
|
||||
// length ratio to border-width at that point.
|
||||
// (see DashedCornerFinder.h for more detail)
|
||||
// Line ends with half segments, to joint with both side easily.
|
||||
//
|
||||
// dotted corner
|
||||
// If both side has same border-width and border-width <= 2.0, draw 1:1
|
||||
// dashed line along the corner.
|
||||
// Otherwise Draw circles along the corner, with appropriate gap that makes
|
||||
// the corner symmetric as far as possible. The size of the circle may
|
||||
// change along the corner, that is tangent to the outer curver and the
|
||||
// inner curve. The ratio of the gap to circle diameter is the maximum
|
||||
// value in [0.5, 1] range.
|
||||
// (see DottedCornerFinder.h for more detail)
|
||||
// Corner ends with filled dots but those dots are drawn by
|
||||
// DrawDashedOrDottedSide. So this may draw no circles if there's no space
|
||||
// between 2 dots at both ends.
|
||||
|
||||
NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED ||
|
||||
mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
|
||||
"Style should be dashed or dotted.");
|
||||
|
||||
if (IsCornerMergeable(aCorner)) {
|
||||
// DrawDashedOrDottedSide will draw corner.
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla::css::Side sideH(GetHorizontalSide(aCorner));
|
||||
mozilla::css::Side sideV(GetVerticalSide(aCorner));
|
||||
Float borderWidthH = mBorderWidths[sideH];
|
||||
Float borderWidthV = mBorderWidths[sideV];
|
||||
if (borderWidthH == 0.0f && borderWidthV == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
Float styleH = mBorderStyles[sideH];
|
||||
Float styleV = mBorderStyles[sideV];
|
||||
|
||||
// Corner between dotted and others with radius=0 is drawn by side.
|
||||
if (IsZeroSize(mBorderRadii[aCorner]) &&
|
||||
(styleV == NS_STYLE_BORDER_STYLE_DOTTED ||
|
||||
styleH == NS_STYLE_BORDER_STYLE_DOTTED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (borderWidthH != borderWidthV || borderWidthH > 2.0f) {
|
||||
uint8_t style = mBorderStyles[aSide];
|
||||
if (style == NS_STYLE_BORDER_STYLE_DOTTED) {
|
||||
DrawDottedCornerSlow(aSide, aCorner);
|
||||
} else {
|
||||
DrawDashedCornerSlow(aSide, aCorner);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nscolor borderColor = mBorderColors[aSide];
|
||||
Point points[4];
|
||||
bool ignored;
|
||||
points[0] = GetStraightBorderPoint(sideH, aCorner, &ignored);
|
||||
points[3] = GetStraightBorderPoint(sideV, aCorner, &ignored);
|
||||
// Round points to draw dot on each pixel.
|
||||
if (borderWidthH < 2.0f) {
|
||||
points[0].x = round(points[0].x);
|
||||
}
|
||||
if (borderWidthV < 2.0f) {
|
||||
points[3].y = round(points[3].y);
|
||||
}
|
||||
points[1] = points[0];
|
||||
points[1].x += kKappaFactor * (points[3].x - points[0].x);
|
||||
points[2] = points[3];
|
||||
points[2].y += kKappaFactor * (points[0].y - points[3].y);
|
||||
|
||||
Float len = GetQuarterEllipticArcLength(fabs(points[0].x - points[3].x),
|
||||
fabs(points[0].y - points[3].y));
|
||||
|
||||
Float dash[2];
|
||||
StrokeOptions strokeOptions(borderWidthH);
|
||||
SetupDashedOptions(&strokeOptions, dash, aSide, len, true);
|
||||
|
||||
RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
|
||||
builder->MoveTo(points[0]);
|
||||
builder->BezierTo(points[1], points[2], points[3]);
|
||||
RefPtr<Path> path = builder->Finish();
|
||||
mDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(borderColor)),
|
||||
strokeOptions);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSBorderRenderer::DrawDottedCornerSlow(mozilla::css::Side aSide,
|
||||
mozilla::css::Corner aCorner)
|
||||
{
|
||||
NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
|
||||
"Style should be dotted.");
|
||||
|
||||
mozilla::css::Side sideH(GetHorizontalSide(aCorner));
|
||||
mozilla::css::Side sideV(GetVerticalSide(aCorner));
|
||||
Float R0 = mBorderWidths[sideH] / 2.0f;
|
||||
Float Rn = mBorderWidths[sideV] / 2.0f;
|
||||
if (R0 == 0.0f && Rn == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
nscolor borderColor = mBorderColors[aSide];
|
||||
Bezier outerBezier;
|
||||
Bezier innerBezier;
|
||||
GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
|
||||
|
||||
bool ignored;
|
||||
Point C0 = GetStraightBorderPoint(sideH, aCorner, &ignored);
|
||||
Point Cn = GetStraightBorderPoint(sideV, aCorner, &ignored);
|
||||
DottedCornerFinder finder(outerBezier, innerBezier, aCorner,
|
||||
mBorderRadii[aCorner].width,
|
||||
mBorderRadii[aCorner].height,
|
||||
C0, R0, Cn, Rn, mBorderCornerDimensions[aCorner]);
|
||||
|
||||
RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
|
||||
while (finder.HasMore()) {
|
||||
DottedCornerFinder::Result result = finder.Next();
|
||||
|
||||
builder->MoveTo(Point(result.C.x + result.r, result.C.y));
|
||||
builder->Arc(result.C, result.r, 0, Float(2.0 * M_PI));
|
||||
}
|
||||
RefPtr<Path> path = builder->Finish();
|
||||
mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSBorderRenderer::DrawDashedCornerSlow(mozilla::css::Side aSide,
|
||||
mozilla::css::Corner aCorner)
|
||||
{
|
||||
NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED,
|
||||
"Style should be dashed.");
|
||||
|
||||
mozilla::css::Side sideH(GetHorizontalSide(aCorner));
|
||||
mozilla::css::Side sideV(GetVerticalSide(aCorner));
|
||||
Float borderWidthH = mBorderWidths[sideH];
|
||||
Float borderWidthV = mBorderWidths[sideV];
|
||||
if (borderWidthH == 0.0f && borderWidthV == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
nscolor borderColor = mBorderColors[aSide];
|
||||
Bezier outerBezier;
|
||||
Bezier innerBezier;
|
||||
GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
|
||||
|
||||
DashedCornerFinder finder(outerBezier, innerBezier,
|
||||
borderWidthH, borderWidthV,
|
||||
mBorderCornerDimensions[aCorner]);
|
||||
|
||||
RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
|
||||
while (finder.HasMore()) {
|
||||
DashedCornerFinder::Result result = finder.Next();
|
||||
|
||||
builder->MoveTo(result.outerSectionBezier.mPoints[0]);
|
||||
builder->BezierTo(result.outerSectionBezier.mPoints[1],
|
||||
result.outerSectionBezier.mPoints[2],
|
||||
result.outerSectionBezier.mPoints[3]);
|
||||
builder->LineTo(result.innerSectionBezier.mPoints[3]);
|
||||
builder->BezierTo(result.innerSectionBezier.mPoints[2],
|
||||
result.innerSectionBezier.mPoints[1],
|
||||
result.innerSectionBezier.mPoints[0]);
|
||||
builder->LineTo(result.outerSectionBezier.mPoints[0]);
|
||||
}
|
||||
|
||||
if (outerBezier.mPoints[0].x != innerBezier.mPoints[0].x) {
|
||||
// Fill gap before the first section.
|
||||
//
|
||||
// outnerPoint[0]
|
||||
// |
|
||||
// v
|
||||
// _+-----------+--
|
||||
// / \##########|
|
||||
// / \#########|
|
||||
// + \########|
|
||||
// |\ \######|
|
||||
// | \ \#####|
|
||||
// | \ \####|
|
||||
// | \ \##|
|
||||
// | \ \#|
|
||||
// | \ \|
|
||||
// | \ _-+--
|
||||
// +--------------+ ^
|
||||
// | | |
|
||||
// | | innerPoint[0]
|
||||
// | |
|
||||
builder->MoveTo(outerBezier.mPoints[0]);
|
||||
builder->LineTo(innerBezier.mPoints[0]);
|
||||
builder->LineTo(Point(innerBezier.mPoints[0].x, outerBezier.mPoints[0].y));
|
||||
builder->LineTo(outerBezier.mPoints[0]);
|
||||
}
|
||||
|
||||
if (outerBezier.mPoints[3].y != innerBezier.mPoints[3].y) {
|
||||
// Fill gap after the last section.
|
||||
//
|
||||
// outnerPoint[3]
|
||||
// |
|
||||
// |
|
||||
// | _+-----------+--
|
||||
// | / \ |
|
||||
// v/ \ |
|
||||
// + \ |
|
||||
// |\ \ |
|
||||
// |##\ \ |
|
||||
// |####\ \ |
|
||||
// |######\ \ |
|
||||
// |########\ \ |
|
||||
// |##########\ \|
|
||||
// |############\ _-+--
|
||||
// +--------------+<-- innerPoint[3]
|
||||
// | |
|
||||
// | |
|
||||
// | |
|
||||
builder->MoveTo(outerBezier.mPoints[3]);
|
||||
builder->LineTo(innerBezier.mPoints[3]);
|
||||
builder->LineTo(Point(outerBezier.mPoints[3].x, innerBezier.mPoints[3].y));
|
||||
builder->LineTo(outerBezier.mPoints[3]);
|
||||
}
|
||||
|
||||
RefPtr<Path> path = builder->Finish();
|
||||
mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSSBorderRenderer::AllBordersSameWidth()
|
||||
{
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "gfxRect.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/BezierUtils.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsColor.h"
|
||||
|
@ -62,7 +61,6 @@ typedef enum {
|
|||
|
||||
class nsCSSBorderRenderer final
|
||||
{
|
||||
typedef mozilla::gfx::Bezier Bezier;
|
||||
typedef mozilla::gfx::ColorPattern ColorPattern;
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
typedef mozilla::gfx::Float Float;
|
||||
|
@ -168,12 +166,6 @@ private:
|
|||
mozilla::css::Corner aCorner,
|
||||
bool* aIsUnfilled);
|
||||
|
||||
// Return bezier control points for the outer and the inner curve for given
|
||||
// corner
|
||||
void GetOuterAndInnerBezier(Bezier* aOuterBezier,
|
||||
Bezier* aInnerBezier,
|
||||
mozilla::css::Corner aCorner);
|
||||
|
||||
// Given a set of sides to fill and a color, do so in the fastest way.
|
||||
//
|
||||
// Stroke tends to be faster for smaller borders because it doesn't go
|
||||
|
@ -207,7 +199,7 @@ private:
|
|||
// Setup the stroke options for the given dashed/dotted side
|
||||
void SetupDashedOptions(StrokeOptions* aStrokeOptions,
|
||||
Float aDash[2], mozilla::css::Side aSide,
|
||||
Float aBorderLength, bool isCorner);
|
||||
Float aBorderLength);
|
||||
|
||||
// Draw the given dashed/dotte side
|
||||
void DrawDashedOrDottedSide(mozilla::css::Side aSide);
|
||||
|
@ -215,18 +207,6 @@ private:
|
|||
// Draw the given dotted side, each dot separately
|
||||
void DrawDottedSideSlow(mozilla::css::Side aSide);
|
||||
|
||||
// Draw the given dashed/dotted corner
|
||||
void DrawDashedOrDottedCorner(mozilla::css::Side aSide,
|
||||
mozilla::css::Corner aCorner);
|
||||
|
||||
// Draw the given dotted corner, each segment separately
|
||||
void DrawDottedCornerSlow(mozilla::css::Side aSide,
|
||||
mozilla::css::Corner aCorner);
|
||||
|
||||
// Draw the given dashed corner, each dot separately
|
||||
void DrawDashedCornerSlow(mozilla::css::Side aSide,
|
||||
mozilla::css::Corner aCorner);
|
||||
|
||||
// Analyze if all border sides have the same width.
|
||||
bool AllBordersSameWidth();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче