Backed out changeset 0863ce40d2f7 (bug 382721)

--HG--
extra : rebase_source : ee6aa275c7ee4291d87a051e92e2e28979b83d96
This commit is contained in:
Carsten "Tomcat" Book 2016-06-10 15:38:37 +02:00
Родитель d02a177069
Коммит 8ec9dd1719
14 изменённых файлов: 11 добавлений и 2625 удалений

Просмотреть файл

@ -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();