mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-10 14:41:50 +00:00
sw_engine: implement stroke cubicto, arcto, lineto
Change-Id: I59e95b1031ebfaf54e966cab334e045613ca3830
This commit is contained in:
parent
6744838453
commit
41dbd9774a
5 changed files with 931 additions and 349 deletions
|
@ -1,5 +1,6 @@
|
|||
source_file = [
|
||||
'tvgSwCommon.h',
|
||||
'tvgSwMath.cpp',
|
||||
'tvgSwRenderer.h',
|
||||
'tvgSwRaster.cpp',
|
||||
'tvgSwRenderer.cpp',
|
||||
|
|
|
@ -27,10 +27,6 @@ constexpr auto SW_CURVE_TYPE_CUBIC = 1;
|
|||
constexpr auto SW_OUTLINE_FILL_WINDING = 0;
|
||||
constexpr auto SW_OUTLINE_FILL_EVEN_ODD = 1;
|
||||
|
||||
constexpr auto SW_STROKE_TAG_ON = 1;
|
||||
constexpr auto SW_STROKE_TAG_BEGIN = 4;
|
||||
constexpr auto SW_STROKE_TAG_END = 8;
|
||||
|
||||
using SwCoord = signed long;
|
||||
using SwFixed = signed long long;
|
||||
|
||||
|
@ -59,6 +55,20 @@ struct SwPoint
|
|||
bool operator!=(const SwPoint& rhs) const {
|
||||
return (x != rhs.x || y != rhs.y);
|
||||
}
|
||||
|
||||
bool zero()
|
||||
{
|
||||
if (x == 0 && y == 0) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool small()
|
||||
{
|
||||
//2 is epsilon...
|
||||
if (abs(x) < 2 && abs(y) < 2) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct SwSize
|
||||
|
@ -141,6 +151,13 @@ struct SwShape
|
|||
SwBBox bbox;
|
||||
};
|
||||
|
||||
|
||||
constexpr static SwFixed ANGLE_PI = (180L << 16);
|
||||
constexpr static SwFixed ANGLE_2PI = (ANGLE_PI << 1);
|
||||
constexpr static SwFixed ANGLE_PI2 = (ANGLE_PI >> 1);
|
||||
constexpr static SwFixed ANGLE_PI4 = (ANGLE_PI >> 2);
|
||||
|
||||
|
||||
static inline SwPoint TO_SWPOINT(const Point* pt)
|
||||
{
|
||||
return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
|
||||
|
@ -153,6 +170,20 @@ static inline SwCoord TO_SWCOORD(float val)
|
|||
}
|
||||
|
||||
|
||||
int64_t mathMultiply(int64_t a, int64_t b);
|
||||
int64_t mathDivide(int64_t a, int64_t b);
|
||||
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
|
||||
void mathRotate(SwPoint& pt, SwFixed angle);
|
||||
SwFixed mathTan(SwFixed angle);
|
||||
SwFixed mathAtan(const SwPoint& pt);
|
||||
SwFixed mathCos(SwFixed angle);
|
||||
SwFixed mathSin(SwFixed angle);
|
||||
void mathSplitCubic(SwPoint* base);
|
||||
SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
|
||||
SwFixed mathLength(SwPoint& pt);
|
||||
bool mathSmallCubic(SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
|
||||
|
||||
void shapeReset(SwShape& sdata);
|
||||
bool shapeGenOutline(const Shape& shape, SwShape& sdata);
|
||||
bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip);
|
||||
|
|
407
src/lib/sw_engine/tvgSwMath.cpp
Normal file
407
src/lib/sw_engine/tvgSwMath.cpp
Normal file
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
#ifndef _TVG_SW_MATH_H_
|
||||
#define _TVG_SW_MATH_H_
|
||||
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
constexpr auto CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
|
||||
|
||||
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
|
||||
constexpr static auto ATAN_MAX = 23;
|
||||
constexpr static SwFixed ATAN_TBL[] = {
|
||||
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
|
||||
14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
|
||||
57L, 29L, 14L, 7L, 4L, 2L, 1L};
|
||||
|
||||
static inline SwCoord SATURATE(const SwCoord x)
|
||||
{
|
||||
return (x >> (sizeof(SwCoord) * 8 - 1));
|
||||
}
|
||||
|
||||
|
||||
static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
|
||||
{
|
||||
return (((x) + ((n)/2)) & ~((n)-1));
|
||||
}
|
||||
|
||||
|
||||
static SwCoord _downscale(SwCoord x)
|
||||
{
|
||||
//multiply a give value by the CORDIC shrink factor
|
||||
|
||||
abs(x);
|
||||
int64_t t = (x * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
|
||||
x = static_cast<SwFixed>(t >> 32);
|
||||
if (x < 0) x = -x;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
static int32_t _normalize(SwPoint& pt)
|
||||
{
|
||||
/* the highest bit in overflow-safe vector components
|
||||
MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
|
||||
constexpr auto SAFE_MSB = 29;
|
||||
|
||||
auto v = pt;
|
||||
|
||||
//High order bit(MSB)
|
||||
//clz: count leading zero’s
|
||||
auto shift = 31 - __builtin_clz(abs(v.x) | abs(v.y));
|
||||
|
||||
if (shift <= SAFE_MSB) {
|
||||
shift = SAFE_MSB - shift;
|
||||
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
|
||||
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
|
||||
} else {
|
||||
shift -= SAFE_MSB;
|
||||
pt.x = v.x >> shift;
|
||||
pt.y = v.y >> shift;
|
||||
shift = -shift;
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
|
||||
static void _polarize(SwPoint& pt)
|
||||
{
|
||||
auto v = pt;
|
||||
SwFixed theta;
|
||||
|
||||
//Get the vector into [-PI/4, PI/4] sector
|
||||
if (v.y > v.x) {
|
||||
if (v.y > -v.x) {
|
||||
auto tmp = v.y;
|
||||
v.y = -v.x;
|
||||
v.x = tmp;
|
||||
theta = ANGLE_PI2;
|
||||
} else {
|
||||
theta = v.y > 0 ? ANGLE_PI : -ANGLE_PI;
|
||||
v.x = -v.x;
|
||||
v.y = -v.y;
|
||||
}
|
||||
} else {
|
||||
if (v.y < -v.x) {
|
||||
theta = -ANGLE_PI2;
|
||||
auto tmp = -v.y;
|
||||
v.y = v.x;
|
||||
v.x = tmp;
|
||||
} else {
|
||||
theta = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto atan = ATAN_TBL;
|
||||
uint32_t i;
|
||||
SwFixed j;
|
||||
|
||||
//Pseudorotations. with right shifts
|
||||
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
|
||||
if (v.y > 0) {
|
||||
auto tmp = v.x + ((v.y + j) >> i);
|
||||
v.y = v.y - ((v.x + j) >> i);
|
||||
v.x = tmp;
|
||||
theta += *atan++;
|
||||
} else {
|
||||
auto tmp = v.x - ((v.y + j) >> i);
|
||||
v.y = v.y + ((v.x + j) >> i);
|
||||
v.x = tmp;
|
||||
theta -= *atan++;
|
||||
}
|
||||
}
|
||||
|
||||
//round theta
|
||||
if (theta >= 0) theta = PAD_ROUND(theta, 32);
|
||||
else theta = -PAD_ROUND(-theta, 32);
|
||||
|
||||
pt.x = v.x;
|
||||
pt.y = theta;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2)
|
||||
{
|
||||
return angle1 + mathDiff(angle1, angle2) / 2;
|
||||
}
|
||||
|
||||
|
||||
bool mathSmallCubic(SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
|
||||
{
|
||||
auto d1 = base[2] - base[3];
|
||||
auto d2 = base[1] - base[2];
|
||||
auto d3 = base[0] - base[1];
|
||||
|
||||
if (d1.small()) {
|
||||
if (d2.small()) {
|
||||
if (d3.small()) {
|
||||
//basically a point.
|
||||
//do nothing to retain original direction
|
||||
} else {
|
||||
angleIn = angleMid = angleOut = mathAtan(d3);
|
||||
}
|
||||
} else {
|
||||
if (d3.small()) {
|
||||
angleIn = angleMid = angleOut = mathAtan(d2);
|
||||
} else {
|
||||
angleIn = angleMid = mathAtan(d2);
|
||||
angleOut = mathAtan(d3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (d2.small()) {
|
||||
if (d3.small()) {
|
||||
angleIn = angleMid = angleOut = mathAtan(d1);
|
||||
} else {
|
||||
angleIn = mathAtan(d1);
|
||||
angleOut = mathAtan(d3);
|
||||
angleMid = mathMean(angleIn, angleOut);
|
||||
}
|
||||
} else {
|
||||
if (d3.small()) {
|
||||
angleIn = mathAtan(d1);
|
||||
angleMid = angleOut = mathAtan(d2);
|
||||
} else {
|
||||
angleIn = mathAtan(d1);
|
||||
angleMid = mathAtan(d2);
|
||||
angleOut = mathAtan(d3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto theta1 = abs(mathDiff(angleIn, angleMid));
|
||||
auto theta2 = abs(mathDiff(angleMid, angleOut));
|
||||
|
||||
if ((theta1 < (ANGLE_PI / 8)) && (theta2 < (ANGLE_PI / 8))) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
|
||||
int64_t mathMultiply(int64_t a, int64_t b)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
int64_t c = (a * b + 0x8000L ) >> 16;
|
||||
return (s > 0) ? c : -c;
|
||||
}
|
||||
|
||||
|
||||
int64_t mathDivide(int64_t a, int64_t b)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
|
||||
return (s < 0 ? -q : q);
|
||||
}
|
||||
|
||||
|
||||
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
if (c < 0) {
|
||||
c = -c;
|
||||
s = -s;
|
||||
}
|
||||
int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL;
|
||||
|
||||
return (s > 0 ? -d : d);
|
||||
}
|
||||
|
||||
|
||||
void mathRotate(SwPoint& pt, SwFixed angle)
|
||||
{
|
||||
if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
|
||||
|
||||
auto v = pt;
|
||||
auto shift = _normalize(v);
|
||||
auto theta = angle;
|
||||
|
||||
//Rotate inside [-PI/4, PI/4] sector
|
||||
while (theta < -ANGLE_PI4) {
|
||||
auto tmp = v.y;
|
||||
v.y = -v.x;
|
||||
v.x = tmp;
|
||||
theta += ANGLE_PI2;
|
||||
}
|
||||
|
||||
while (theta > ANGLE_PI4) {
|
||||
auto tmp = -v.y;
|
||||
v.y = v.x;
|
||||
v.x = tmp;
|
||||
theta -= ANGLE_PI2;
|
||||
}
|
||||
|
||||
auto atan = ATAN_TBL;
|
||||
uint32_t i;
|
||||
SwFixed j;
|
||||
|
||||
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
|
||||
if (theta < 0) {
|
||||
auto tmp = v.x + ((v.y + j) >> i);
|
||||
v.y = v.y - ((v.x + j) >> i);
|
||||
v.x = tmp;
|
||||
theta += *atan++;
|
||||
}else {
|
||||
auto tmp = v.x - ((v.y + j) >> i);
|
||||
v.y = v.y + ((v.x + j) >> i);
|
||||
v.x = tmp;
|
||||
theta -= *atan++;
|
||||
}
|
||||
}
|
||||
|
||||
v.x = _downscale(v.x);
|
||||
v.y = _downscale(v.y);
|
||||
|
||||
if (shift > 0) {
|
||||
auto half = static_cast<int32_t>(1L << (shift - 1));
|
||||
v.x = (v.x + half + SATURATE(v.x)) >> shift;
|
||||
v.y = (v.y + half + SATURATE(v.y)) >> shift;
|
||||
} else {
|
||||
shift = -shift;
|
||||
v.x = static_cast<SwCoord>((unsigned long)v.x << shift);
|
||||
v.y = static_cast<SwCoord>((unsigned long)v.y << shift);
|
||||
}
|
||||
}
|
||||
|
||||
SwFixed mathTan(SwFixed angle)
|
||||
{
|
||||
SwPoint v = {CORDIC_FACTOR >> 8, 0};
|
||||
mathRotate(v, angle);
|
||||
return mathDivide(v.y, v.x);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathAtan(const SwPoint& pt)
|
||||
{
|
||||
if (pt.x == 0 && pt.y == 0) return 0;
|
||||
|
||||
auto v = pt;
|
||||
_normalize(v);
|
||||
_polarize(v);
|
||||
|
||||
return v.y;
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathSin(SwFixed angle)
|
||||
{
|
||||
return mathCos(ANGLE_PI2 - angle);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathCos(SwFixed angle)
|
||||
{
|
||||
SwPoint v = {CORDIC_FACTOR >> 8, 0};
|
||||
mathRotate(v, angle);
|
||||
return (v.x + 0x80L) >> 8;
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathLength(SwPoint& pt)
|
||||
{
|
||||
auto v = pt;
|
||||
|
||||
//trivial case
|
||||
if (v.x == 0) return abs(v.y);
|
||||
if (v.y == 0) return abs(v.x);
|
||||
|
||||
//general case
|
||||
auto shift = _normalize(v);
|
||||
_polarize(v);
|
||||
v.x = _downscale(v.x);
|
||||
|
||||
if (shift > 0) return (v.x + (1 << (shift -1))) >> shift;
|
||||
return static_cast<SwFixed>((uint32_t)v.x << -shift);
|
||||
}
|
||||
|
||||
|
||||
void mathSplitCubic(SwPoint* base)
|
||||
{
|
||||
assert(base);
|
||||
|
||||
SwCoord a, b, c, d;
|
||||
|
||||
base[6].x = base[3].x;
|
||||
c = base[1].x;
|
||||
d = base[2].x;
|
||||
base[1].x = a = (base[0].x + c) / 2;
|
||||
base[5].x = b = (base[3].x + d) / 2;
|
||||
c = (c + d) / 2;
|
||||
base[2].x = a = (a + c) / 2;
|
||||
base[4].x = b = (b + c) / 2;
|
||||
base[3].x = (a + b) / 2;
|
||||
|
||||
base[6].y = base[3].y;
|
||||
c = base[1].y;
|
||||
d = base[2].y;
|
||||
base[1].y = a = (base[0].y + c) / 2;
|
||||
base[5].y = b = (base[3].y + d) / 2;
|
||||
c = (c + d) / 2;
|
||||
base[2].y = a = (a + c) / 2;
|
||||
base[4].y = b = (b + c) / 2;
|
||||
base[3].y = (a + b) / 2;
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
|
||||
{
|
||||
auto delta = angle2 - angle1;
|
||||
|
||||
delta %= ANGLE_2PI;
|
||||
if (delta < 0) delta += ANGLE_2PI;
|
||||
if (delta > ANGLE_PI) delta -= ANGLE_2PI;
|
||||
|
||||
return delta;
|
||||
}
|
||||
#endif /* _TVG_SW_MATH_H_ */
|
|
@ -490,34 +490,6 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
|
|||
}
|
||||
|
||||
|
||||
static void _splitCubic(SwPoint* base)
|
||||
{
|
||||
assert(base);
|
||||
|
||||
SwCoord a, b, c, d;
|
||||
|
||||
base[6].x = base[3].x;
|
||||
c = base[1].x;
|
||||
d = base[2].x;
|
||||
base[1].x = a = (base[0].x + c) / 2;
|
||||
base[5].x = b = (base[3].x + d) / 2;
|
||||
c = (c + d) / 2;
|
||||
base[2].x = a = (a + c) / 2;
|
||||
base[4].x = b = (b + c) / 2;
|
||||
base[3].x = (a + b) / 2;
|
||||
|
||||
base[6].y = base[3].y;
|
||||
c = base[1].y;
|
||||
d = base[2].y;
|
||||
base[1].y = a = (base[0].y + c) / 2;
|
||||
base[5].y = b = (base[3].y + d) / 2;
|
||||
c = (c + d) / 2;
|
||||
base[2].y = a = (a + c) / 2;
|
||||
base[4].y = b = (b + c) / 2;
|
||||
base[3].y = (a + b) / 2;
|
||||
}
|
||||
|
||||
|
||||
static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
{
|
||||
auto arc = rw.bezStack;
|
||||
|
@ -579,7 +551,7 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2,
|
|||
goto draw;
|
||||
}
|
||||
split:
|
||||
_splitCubic(arc);
|
||||
mathSplitCubic(arc);
|
||||
arc += 3;
|
||||
continue;
|
||||
|
||||
|
|
|
@ -23,225 +23,17 @@
|
|||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
constexpr auto CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
|
||||
constexpr static SwFixed ANGLE_PI = (180L << 16);
|
||||
constexpr static SwFixed ANGLE_2PI = (ANGLE_PI << 1);
|
||||
constexpr static SwFixed ANGLE_PI2 = (ANGLE_PI >> 1);
|
||||
constexpr static SwFixed ANGLE_PI4 = (ANGLE_PI >> 2);
|
||||
static constexpr auto SW_STROKE_TAG_ON = 1;
|
||||
static constexpr auto SW_STROKE_TAG_CUBIC = 2;
|
||||
static constexpr auto SW_STROKE_TAG_BEGIN = 4;
|
||||
static constexpr auto SW_STROKE_TAG_END = 8;
|
||||
|
||||
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
|
||||
constexpr static auto ATAN_MAX = 23;
|
||||
constexpr static SwFixed ATAN_TBL[] = {
|
||||
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
|
||||
14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
|
||||
57L, 29L, 14L, 7L, 4L, 2L, 1L};
|
||||
|
||||
|
||||
static inline SwCoord SATURATE(const SwCoord x)
|
||||
{
|
||||
return (x >> (sizeof(long) * 8 - 1));
|
||||
}
|
||||
|
||||
|
||||
static inline SwFixed SIDE_TO_ROTATE(int32_t s)
|
||||
static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
|
||||
{
|
||||
return (ANGLE_PI2 - (s) * ANGLE_PI);
|
||||
}
|
||||
|
||||
|
||||
static int64_t _multiply(int64_t a, int64_t b)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
//move sign
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
int64_t c = (a * b + 0x8000L ) >> 16;
|
||||
return (s > 0) ? c : -c;
|
||||
}
|
||||
|
||||
|
||||
static int64_t _divide(int64_t a, int64_t b)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
//move sign
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
|
||||
return (s < 0 ? -q : q);
|
||||
}
|
||||
|
||||
|
||||
static SwFixed _angleDiff(SwFixed angle1, SwFixed angle2)
|
||||
{
|
||||
auto delta = angle2 - angle1;
|
||||
|
||||
delta %= ANGLE_2PI;
|
||||
if (delta < 0) delta += ANGLE_2PI;
|
||||
if (delta > ANGLE_PI) delta -= ANGLE_2PI;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
|
||||
static void _trigDownscale(SwPoint& pt)
|
||||
{
|
||||
//multiply a give value by the CORDIC shrink factor
|
||||
|
||||
auto s = pt;
|
||||
|
||||
//abs
|
||||
if (pt.x < 0) pt.x = -pt.x;
|
||||
if (pt.y < 0) pt.y = -pt.y;
|
||||
|
||||
int64_t vx = (pt.x * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
|
||||
int64_t vy = (pt.y * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
|
||||
|
||||
pt.x = static_cast<SwFixed>(vx >> 32);
|
||||
pt.y = static_cast<SwFixed>(vy >> 32);
|
||||
|
||||
if (s.x < 0) pt.x = -pt.x;
|
||||
if (s.y < 0) pt.y = -pt.y;
|
||||
}
|
||||
|
||||
|
||||
static int32_t _trigPrenorm(SwPoint& pt)
|
||||
{
|
||||
/* the highest bit in overflow-safe vector components
|
||||
MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
|
||||
constexpr auto TRIG_SAFE_MSB = 29;
|
||||
|
||||
auto v = pt;
|
||||
|
||||
//High order bit(MSB)
|
||||
//clz: count leading zero’s
|
||||
auto shift = 31 - __builtin_clz(abs(v.x) | abs(v.y));
|
||||
|
||||
if (shift <= TRIG_SAFE_MSB) {
|
||||
shift = TRIG_SAFE_MSB - shift;
|
||||
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
|
||||
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
|
||||
} else {
|
||||
shift -= TRIG_SAFE_MSB;
|
||||
pt.x = v.x >> shift;
|
||||
pt.y = v.y >> shift;
|
||||
shift = -shift;
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
|
||||
static void _trigPseudoRotate(SwPoint& pt, SwFixed theta)
|
||||
{
|
||||
auto v = pt;
|
||||
|
||||
//Rotate inside [-PI/4, PI/4] sector
|
||||
while (theta < -ANGLE_PI4) {
|
||||
auto temp = v.y;
|
||||
v.y = -v.x;
|
||||
v.x = temp;
|
||||
theta += ANGLE_PI2;
|
||||
}
|
||||
|
||||
while (theta > ANGLE_PI4) {
|
||||
auto temp = -v.y;
|
||||
v.y = v.x;
|
||||
v.x = temp;
|
||||
theta -= ANGLE_PI2;
|
||||
}
|
||||
|
||||
auto atan = ATAN_TBL;
|
||||
uint32_t i;
|
||||
SwFixed j;
|
||||
|
||||
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
|
||||
if (theta < 0) {
|
||||
auto temp = v.x + ((v.y + j) >> i);
|
||||
v.y = v.y - ((v.x + j) >> i);
|
||||
v.x = temp;
|
||||
theta += *atan++;
|
||||
}else {
|
||||
auto temp = v.x - ((v.y + j) >> i);
|
||||
v.y = v.y + ((v.x + j) >> i);
|
||||
v.x = temp;
|
||||
theta -= *atan++;
|
||||
}
|
||||
}
|
||||
|
||||
pt = v;
|
||||
}
|
||||
|
||||
|
||||
static void _rotate(SwPoint& pt, SwFixed angle)
|
||||
{
|
||||
if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
|
||||
|
||||
auto v = pt;
|
||||
auto shift = _trigPrenorm(v);
|
||||
_trigPseudoRotate(v, angle);
|
||||
_trigDownscale(v);
|
||||
|
||||
if (shift > 0) {
|
||||
auto half = static_cast<int32_t>(1L << (shift - 1));
|
||||
v.x = (v.x + half + SATURATE(v.x)) >> shift;
|
||||
v.y = (v.y + half + SATURATE(v.y)) >> shift;
|
||||
} else {
|
||||
shift = -shift;
|
||||
v.x = static_cast<SwCoord>((unsigned long)v.x << shift);
|
||||
v.y = static_cast<SwCoord>((unsigned long)v.y << shift);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static SwFixed _tan(SwFixed angle)
|
||||
{
|
||||
SwPoint v = {CORDIC_FACTOR >> 8, 0};
|
||||
_rotate(v, angle);
|
||||
return _divide(v.y, v.x);
|
||||
}
|
||||
|
||||
|
||||
static SwFixed _cos(SwFixed angle)
|
||||
{
|
||||
SwPoint v = {CORDIC_FACTOR >> 8, 0};
|
||||
_rotate(v, angle);
|
||||
return (v.x + 0x80L) >> 8;
|
||||
}
|
||||
|
||||
|
||||
static void _lineTo(SwStroke& stroke, const SwPoint& to)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void _arcTo(SwStroke& stroke, int32_t side)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
|
||||
{
|
||||
auto maxOld = border->maxPts;
|
||||
|
@ -266,11 +58,130 @@ static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
|
|||
}
|
||||
|
||||
|
||||
static void _borderClose(SwStrokeBorder* border, bool reverse)
|
||||
{
|
||||
assert(border && border->start >= 0);
|
||||
|
||||
uint32_t start = border->start;
|
||||
uint32_t count = border->ptsCnt;
|
||||
|
||||
//Don't record empty paths!
|
||||
if (count <= start + 1U) {
|
||||
border->ptsCnt = start;
|
||||
} else {
|
||||
/* Copy the last point to the start of this sub-path,
|
||||
since it contains the adjusted starting coordinates */
|
||||
border->ptsCnt = --count;
|
||||
border->pts[start] = border->pts[count];
|
||||
|
||||
if (reverse) {
|
||||
//reverse the points
|
||||
auto pt1 = border->pts + start + 1;
|
||||
auto pt2 = border->pts + count - 1;
|
||||
|
||||
while (pt1 < pt2) {
|
||||
auto tmp = *pt1;
|
||||
*pt1 = *pt2;
|
||||
*pt2 = tmp;
|
||||
++pt1;
|
||||
--pt2;
|
||||
}
|
||||
|
||||
//reverse the tags
|
||||
auto tag1 = border->tags + start + 1;
|
||||
auto tag2 = border->tags + count - 1;
|
||||
|
||||
while (tag1 < tag2) {
|
||||
auto tmp = *tag1;
|
||||
*tag1 = *tag2;
|
||||
*tag2 = tmp;
|
||||
++tag1;
|
||||
--tag2;
|
||||
}
|
||||
}
|
||||
|
||||
border->tags[start] |= SW_STROKE_TAG_BEGIN;
|
||||
border->tags[count - 1] |= SW_STROKE_TAG_END;
|
||||
}
|
||||
|
||||
border->start = -1;
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to)
|
||||
{
|
||||
assert(border->start >= 0);
|
||||
|
||||
_growBorder(border, 3);
|
||||
|
||||
auto pt = border->pts + border->ptsCnt;
|
||||
auto tag = border->tags + border->ptsCnt;
|
||||
|
||||
pt[0] = ctrl1;
|
||||
pt[1] = ctrl2;
|
||||
pt[2] = to;
|
||||
|
||||
tag[0] = SW_STROKE_TAG_CUBIC;
|
||||
tag[1] = SW_STROKE_TAG_CUBIC;
|
||||
tag[2] = SW_STROKE_TAG_ON;
|
||||
|
||||
border->ptsCnt += 3;
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff)
|
||||
{
|
||||
constexpr auto ARC_CUBIC_ANGLE = ANGLE_PI / 2;
|
||||
SwPoint a = {radius, 0};
|
||||
mathRotate(a, angleStart);
|
||||
a += center;
|
||||
|
||||
auto total = angleDiff;
|
||||
auto angle = angleStart;
|
||||
auto rotate = (angleDiff >= 0) ? ANGLE_PI2 : -ANGLE_PI2;
|
||||
|
||||
while (total != 0) {
|
||||
auto step = total;
|
||||
if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE;
|
||||
else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE;
|
||||
|
||||
auto next = angle + step;
|
||||
auto theta = step;
|
||||
if (theta < 0) theta = -theta;
|
||||
|
||||
theta >>= 1;
|
||||
|
||||
//compute end point
|
||||
SwPoint b = {radius, 0};
|
||||
mathRotate(b, next);
|
||||
b += center;
|
||||
|
||||
//compute first and second control points
|
||||
auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3);
|
||||
|
||||
SwPoint a2 = {length, 0};
|
||||
mathRotate(a2, angle + rotate);
|
||||
a2 += a;
|
||||
|
||||
SwPoint b2 = {length, 0};
|
||||
mathRotate(b2, next - rotate);
|
||||
b2 += b;
|
||||
|
||||
//add cubic arc
|
||||
_borderCubicTo(border, a2, b2, b);
|
||||
|
||||
//process the rest of the arc?
|
||||
a = b;
|
||||
total -= step;
|
||||
angle = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable)
|
||||
{
|
||||
constexpr SwCoord EPSILON = 2;
|
||||
|
||||
assert(border && border->start >= 0);
|
||||
|
||||
if (border->movable) {
|
||||
|
@ -279,7 +190,7 @@ static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable)
|
|||
} else {
|
||||
//don't add zero-length line_to
|
||||
auto diff = border->pts[border->ptsCnt - 1] - to;
|
||||
if (border->ptsCnt > 0 && abs(diff.x) < EPSILON && abs(diff.y) < EPSILON) return;
|
||||
if (border->ptsCnt > 0 && diff.small()) return;
|
||||
|
||||
_growBorder(border, 1);
|
||||
border->pts[border->ptsCnt] = to;
|
||||
|
@ -291,6 +202,356 @@ static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable)
|
|||
}
|
||||
|
||||
|
||||
static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
|
||||
{
|
||||
assert(border);
|
||||
|
||||
//close current open path if any?
|
||||
if (border->start >= 0)
|
||||
_borderClose(border, false);
|
||||
|
||||
border->start = border->ptsCnt;
|
||||
border->movable = false;
|
||||
|
||||
_borderLineTo(border, to, false);
|
||||
}
|
||||
|
||||
|
||||
static void _arcTo(SwStroke& stroke, int32_t side)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto total = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
if (total == ANGLE_PI) total = -rotate * 2;
|
||||
|
||||
_borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total);
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||
{
|
||||
constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
|
||||
|
||||
assert(MITER_LIMIT >= 0x10000);
|
||||
|
||||
auto border = stroke.borders + side;
|
||||
assert(border);
|
||||
|
||||
if (stroke.join == StrokeJoin::Round) {
|
||||
_arcTo(stroke, side);
|
||||
} else {
|
||||
//this is a mitered (pointed) or beveled (truncated) corner
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto bevel = (stroke.join == StrokeJoin::Bevel) ? true : false;
|
||||
SwFixed phi = 0;
|
||||
SwFixed thcos = 0;
|
||||
|
||||
if (!bevel) {
|
||||
auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
if (theta == ANGLE_PI) {
|
||||
theta = rotate;
|
||||
phi = stroke.angleIn;
|
||||
} else {
|
||||
theta /= 2;
|
||||
phi = stroke.angleIn + theta + rotate;
|
||||
}
|
||||
|
||||
thcos = mathCos(theta);
|
||||
auto sigma = mathMultiply(MITER_LIMIT, thcos);
|
||||
|
||||
//is miter limit exceeded?
|
||||
if (sigma < 0x10000L) bevel = true;
|
||||
}
|
||||
|
||||
//this is a bevel (broken angle)
|
||||
if (bevel) {
|
||||
SwPoint delta = {stroke.width, 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
delta += stroke.center;
|
||||
border->movable = false;
|
||||
_borderLineTo(border, delta, false);
|
||||
//this is a miter (intersection)
|
||||
} else {
|
||||
auto length = mathDivide(stroke.width, thcos);
|
||||
SwPoint delta = {length, 0};
|
||||
mathRotate(delta, phi);
|
||||
delta += stroke.center;
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
/* Now add and end point
|
||||
Only needed if not lineto (lineLength is zero for curves) */
|
||||
if (lineLength == 0) {
|
||||
delta = {stroke.width, 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
delta += stroke.center;
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2;
|
||||
SwPoint delta;
|
||||
bool intersect;
|
||||
|
||||
/* Only intersect borders if between two line_to's and both
|
||||
lines are long enough (line length is zero fur curves). */
|
||||
if (!border->movable || lineLength == 0) {
|
||||
intersect = false;
|
||||
} else {
|
||||
//compute minimum required length of lines
|
||||
SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta)));
|
||||
if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
|
||||
}
|
||||
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
|
||||
if (!intersect) {
|
||||
delta = {stroke.width, 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
delta += stroke.center;
|
||||
border->movable = false;
|
||||
} else {
|
||||
//compute median angle
|
||||
delta = {mathDivide(stroke.width, mathCos(theta)), 0};
|
||||
mathRotate(delta, stroke.angleIn + theta + rotate);
|
||||
delta += stroke.center;
|
||||
}
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
|
||||
|
||||
void _processCorner(SwStroke& stroke, SwFixed lineLength)
|
||||
{
|
||||
auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
|
||||
//no specific corner processing is required if the turn is 0
|
||||
if (turn == 0) return;
|
||||
|
||||
//when we turn to the right, the inside side is 0
|
||||
int32_t inside = 0;
|
||||
|
||||
//otherwise, the inside is 1
|
||||
if (turn < 0) inside = 1;
|
||||
|
||||
//process the inside
|
||||
_inside(stroke, inside, lineLength);
|
||||
|
||||
//process the outside
|
||||
_outside(stroke, 1 - inside, lineLength);
|
||||
}
|
||||
|
||||
|
||||
void _subPathStart(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
|
||||
{
|
||||
SwPoint delta = {stroke.width, 0};
|
||||
mathRotate(delta, startAngle + ANGLE_PI2);
|
||||
|
||||
auto pt = stroke.center + delta;
|
||||
auto border = stroke.borders;
|
||||
_borderMoveTo(border, pt);
|
||||
|
||||
pt = stroke.center - delta;
|
||||
++border;
|
||||
_borderMoveTo(border, pt);
|
||||
|
||||
/* Save angle, position and line length for last join
|
||||
lineLength is zero for curves */
|
||||
stroke.subPathAngle = startAngle;
|
||||
stroke.firstPt = false;
|
||||
stroke.subPathLineLength = lineLength;
|
||||
}
|
||||
|
||||
|
||||
static void _lineTo(SwStroke& stroke, const SwPoint& to)
|
||||
{
|
||||
auto delta = to - stroke.center;
|
||||
|
||||
//a zero-length lineto is a no-op; avoid creating a spurious corner
|
||||
if (delta.zero()) return;
|
||||
|
||||
//compute length of line
|
||||
auto lineLength = mathLength(delta);
|
||||
auto angle = mathAtan(delta);
|
||||
|
||||
delta = {stroke.width, 0};
|
||||
mathRotate(delta, angle + ANGLE_PI2);
|
||||
|
||||
//process corner if necessary
|
||||
if (stroke.firstPt) {
|
||||
/* This is the first segment of a subpath. We need to add a point to each border
|
||||
at their respective starting point locations. */
|
||||
_subPathStart(stroke, angle, lineLength);
|
||||
} else {
|
||||
//process the current corner
|
||||
stroke.angleOut = angle;
|
||||
_processCorner(stroke, lineLength);
|
||||
}
|
||||
|
||||
//now add a line segment to both the inside and outside paths
|
||||
auto border = stroke.borders;
|
||||
auto side = 1;
|
||||
|
||||
while (side >= 0) {
|
||||
auto pt = to + delta;
|
||||
|
||||
//the ends of lineto borders are movable
|
||||
_borderLineTo(border, pt, true);
|
||||
|
||||
delta.x = -delta.x;
|
||||
delta.y = -delta.y;
|
||||
|
||||
--side;
|
||||
++border;
|
||||
}
|
||||
|
||||
stroke.angleIn = angle;
|
||||
stroke.center = to;
|
||||
stroke.lineLength = lineLength;
|
||||
}
|
||||
|
||||
|
||||
static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
{
|
||||
/* if all control points are coincident, this is a no-op;
|
||||
avoid creating a spurious corner */
|
||||
if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
|
||||
stroke.center = to;
|
||||
return;
|
||||
}
|
||||
|
||||
SwPoint bezStack[37]; //TODO: static?
|
||||
auto firstArc = true;
|
||||
auto limit = bezStack + 32;
|
||||
auto arc = bezStack;
|
||||
arc[0] = to;
|
||||
arc[1] = ctrl2;
|
||||
arc[2] = ctrl1;
|
||||
arc[3] = stroke.center;
|
||||
|
||||
while (arc >= bezStack) {
|
||||
SwFixed angleIn, angleOut, angleMid;
|
||||
|
||||
//initialize with current direction
|
||||
angleIn = angleOut = angleMid = stroke.angleIn;
|
||||
|
||||
if (arc < limit && mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
|
||||
if (stroke.firstPt) stroke.angleIn = angleIn;
|
||||
mathSplitCubic(arc);
|
||||
arc += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstArc) {
|
||||
firstArc = false;
|
||||
//process corner if necessary
|
||||
if (stroke.firstPt) {
|
||||
_subPathStart(stroke, angleIn, 0);
|
||||
} else {
|
||||
stroke.angleOut = angleIn;
|
||||
_processCorner(stroke, 0);
|
||||
}
|
||||
} else if (abs(mathDiff(stroke.angleIn, angleIn)) > (ANGLE_PI / 8)) {
|
||||
//if the deviation from one arc to the next is too great add a round corner
|
||||
stroke.center = arc[3];
|
||||
stroke.angleOut = angleIn;
|
||||
stroke.join = StrokeJoin::Round;
|
||||
|
||||
_processCorner(stroke, 0);
|
||||
|
||||
//reinstate line join style
|
||||
stroke.join = stroke.joinSaved;
|
||||
}
|
||||
|
||||
//the arc's angle is small enough; we can add it directly to each border
|
||||
auto theta1 = mathDiff(angleIn, angleMid) / 2;
|
||||
auto theta2 = mathDiff(angleMid, angleOut) / 2;
|
||||
auto phi1 = mathMean(angleIn, angleMid);
|
||||
auto phi2 = mathMean(angleMid, angleOut);
|
||||
auto length1 = mathDivide(stroke.width, mathCos(theta1));
|
||||
auto length2 = mathDivide(stroke.width, mathCos(theta2));
|
||||
SwFixed alpha0 = 0;
|
||||
|
||||
//compute direction of original arc
|
||||
if (stroke.handleWideStrokes) {
|
||||
alpha0 = mathAtan(arc[0] - arc[3]);
|
||||
}
|
||||
|
||||
auto border = stroke.borders;
|
||||
int32_t side = 0;
|
||||
|
||||
while (side <= 1)
|
||||
{
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
|
||||
//compute control points
|
||||
SwPoint _ctrl1 = {length1, 0};
|
||||
mathRotate(_ctrl1, phi1 + rotate);
|
||||
_ctrl1 += arc[2];
|
||||
|
||||
SwPoint _ctrl2 = {length2, 0};
|
||||
mathRotate(_ctrl2, phi2 + rotate);
|
||||
_ctrl2 += arc[1];
|
||||
|
||||
//compute end point
|
||||
SwPoint _end = {stroke.width, 0};
|
||||
mathRotate(_end, angleOut + rotate);
|
||||
_end += arc[0];
|
||||
|
||||
if (stroke.handleWideStrokes) {
|
||||
|
||||
/* determine whether the border radius is greater than the radius of
|
||||
curvature of the original arc */
|
||||
auto _start = border->pts[border->ptsCnt - 1];
|
||||
auto alpha1 = mathAtan(_end - _start);
|
||||
|
||||
//is the direction of the border arc opposite to that of the original arc?
|
||||
if (abs(mathDiff(alpha0, alpha1)) > ANGLE_PI / 2) {
|
||||
|
||||
//use the sine rule to find the intersection point
|
||||
auto beta = mathAtan(arc[3] - _start);
|
||||
auto gamma = mathAtan(arc[0] - _end);
|
||||
auto bvec = _end - _start;
|
||||
auto blen = mathLength(bvec);
|
||||
auto sinA = abs(mathSin(alpha1 - gamma));
|
||||
auto sinB = abs(mathSin(beta - gamma));
|
||||
auto alen = mathMulDiv(blen, sinA, sinB);
|
||||
|
||||
SwPoint delta = {alen, 0};
|
||||
mathRotate(delta, beta);
|
||||
delta += _start;
|
||||
|
||||
//circumnavigate the negative sector backwards
|
||||
border->movable = false;
|
||||
_borderLineTo(border, delta, false);
|
||||
_borderLineTo(border, _end, false);
|
||||
_borderCubicTo(border, _ctrl2, _ctrl1, _start);
|
||||
|
||||
//and thenmove to the endpoint
|
||||
_borderLineTo(border, _end, false);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//else fall through
|
||||
}
|
||||
_borderCubicTo(border, _ctrl1, _ctrl2, _end);
|
||||
++side;
|
||||
++border;
|
||||
}
|
||||
arc -= 3;
|
||||
stroke.angleIn = angleOut;
|
||||
}
|
||||
stroke.center = to;
|
||||
}
|
||||
|
||||
|
||||
static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
|
||||
{
|
||||
if (stroke.cap == StrokeCap::Square) {
|
||||
|
@ -298,20 +559,20 @@ static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
|
|||
auto border = stroke.borders + side;
|
||||
|
||||
SwPoint delta = {stroke.width, 0};
|
||||
_rotate(delta, angle);
|
||||
mathRotate(delta, angle);
|
||||
|
||||
SwPoint delta2 = {stroke.width, 0};
|
||||
_rotate(delta2, angle + rotate);
|
||||
mathRotate(delta2, angle + rotate);
|
||||
|
||||
delta += stroke.center + delta2;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
delta = {stroke.width, 0};
|
||||
_rotate(delta, angle);
|
||||
mathRotate(delta, angle);
|
||||
|
||||
delta2 = {stroke.width, 0};
|
||||
_rotate(delta2, angle - rotate);
|
||||
mathRotate(delta2, angle - rotate);
|
||||
|
||||
delta += delta2 + stroke.center;
|
||||
|
||||
|
@ -329,14 +590,14 @@ static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
|
|||
auto border = stroke.borders + side;
|
||||
|
||||
SwPoint delta = {stroke.width, 0};
|
||||
_rotate(delta, angle + rotate);
|
||||
mathRotate(delta, angle + rotate);
|
||||
|
||||
delta += stroke.center;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
delta = {stroke.width, 0};
|
||||
_rotate(delta, angle - rotate);
|
||||
mathRotate(delta, angle - rotate);
|
||||
|
||||
delta += stroke.center;
|
||||
|
||||
|
@ -389,88 +650,6 @@ static void _addReverseLeft(SwStroke& stroke, bool opened)
|
|||
}
|
||||
|
||||
|
||||
static void _closeBorder(SwStrokeBorder* border, bool reverse)
|
||||
{
|
||||
assert(border && border->start >= 0);
|
||||
|
||||
uint32_t start = border->start;
|
||||
uint32_t count = border->ptsCnt;
|
||||
|
||||
//Don't record empty paths!
|
||||
if (count <= start + 1U) {
|
||||
border->ptsCnt = start;
|
||||
} else {
|
||||
/* Copy the last point to the start of this sub-path,
|
||||
since it contains the adjusted starting coordinates */
|
||||
border->ptsCnt = --count;
|
||||
border->pts[start] = border->pts[count];
|
||||
|
||||
if (reverse) {
|
||||
//reverse the points
|
||||
auto pt1 = border->pts + start + 1;
|
||||
auto pt2 = border->pts + count - 1;
|
||||
|
||||
for (; pt1 < pt2; pt1++, pt2--) {
|
||||
auto tmp = *pt1;
|
||||
*pt1 = *pt2;
|
||||
*pt2 = tmp;
|
||||
}
|
||||
|
||||
//reverse the tags
|
||||
auto tag1 = border->tags + start + 1;
|
||||
auto tag2 = border->tags + count - 1;
|
||||
|
||||
for (; tag1 < tag2; tag1++, tag2++) {
|
||||
auto tmp = *tag1;
|
||||
*tag1 = *tag2;
|
||||
*tag2 = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
border->tags[start] |= SW_STROKE_TAG_BEGIN;
|
||||
border->tags[count - 1] |= SW_STROKE_TAG_END;
|
||||
}
|
||||
|
||||
border->start = -1;
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
auto theta = _angleDiff(stroke.angleIn, stroke.angleOut) / 2;
|
||||
SwPoint delta;
|
||||
bool intersect;
|
||||
|
||||
/* Only intersect borders if between two line_to's and both
|
||||
lines are long enough (line length is zero fur curves). */
|
||||
if (!border->movable || lineLength == 0) {
|
||||
intersect = false;
|
||||
} else {
|
||||
//compute minimum required length of lines
|
||||
SwFixed minLength = abs(_multiply(stroke.width, _tan(theta)));
|
||||
if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
|
||||
}
|
||||
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
|
||||
if (!intersect) {
|
||||
delta = {stroke.width, 0};
|
||||
_rotate(delta, stroke.angleOut + rotate);
|
||||
delta += stroke.center;
|
||||
border->movable = false;
|
||||
} else {
|
||||
//compute median angle
|
||||
delta = {_divide(stroke.width, _cos(theta)), 0};
|
||||
_rotate(delta, stroke.angleIn + theta + rotate);
|
||||
delta += stroke.center;
|
||||
}
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
|
||||
|
||||
static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened)
|
||||
{
|
||||
cout << "stroke opened? = " << opened << endl;
|
||||
|
@ -518,7 +697,7 @@ static void _endSubPath(SwStroke& stroke)
|
|||
|
||||
/* now end the right subpath accordingly. The left one is rewind
|
||||
and deosn't need further processing */
|
||||
_closeBorder(right, false);
|
||||
_borderClose(right, false);
|
||||
} else {
|
||||
|
||||
//close the path if needed
|
||||
|
@ -527,7 +706,7 @@ static void _endSubPath(SwStroke& stroke)
|
|||
|
||||
//process the corner
|
||||
stroke.angleOut = stroke.subPathAngle;
|
||||
auto turn = _angleDiff(stroke.angleIn, stroke.angleOut);
|
||||
auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
|
||||
//No specific corner processing is required if the turn is 0
|
||||
if (turn != 0) {
|
||||
|
@ -542,8 +721,8 @@ static void _endSubPath(SwStroke& stroke)
|
|||
_inside(stroke, 1 - inside, stroke.subPathLineLength); //outside
|
||||
}
|
||||
|
||||
_closeBorder(stroke.borders + 0, false);
|
||||
_closeBorder(stroke.borders + 1, true);
|
||||
_borderClose(stroke.borders + 0, false);
|
||||
_borderClose(stroke.borders + 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -572,14 +751,6 @@ void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join)
|
|||
{
|
||||
_deleteRle(stroke);
|
||||
|
||||
#if 0
|
||||
miterLimit = 4 * (1 >> 16);
|
||||
|
||||
/* ensure miter limit has sensible value */
|
||||
if ( stroker->miter_limit < 0x10000 )
|
||||
stroker->miter_limit = 0x10000;
|
||||
#endif
|
||||
|
||||
stroke.width = TO_SWCOORD(width * 0.5f);
|
||||
stroke.cap = cap;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue