1 //========================================================================
5 //========================================================================
7 //========================================================================
9 // Modified under the Poppler project - http://poppler.freedesktop.org
11 // All changes made under the Poppler project to this file are licensed
12 // under GPL version 2 or later
14 // Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha@gmail.com>
15 // Copyright (C) 2010, 2011 Albert Astals Cid <aacid@kde.org>
16 // Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
18 // To see a description of the changes please see the Changelog file that
19 // came with your tarball or type make ChangeLog if you are building from git
21 //========================================================================
25 #ifdef USE_GCC_PRAGMAS
26 #pragma implementation
33 #include "SplashMath.h"
34 #include "SplashPath.h"
35 #include "SplashXPath.h"
37 //------------------------------------------------------------------------
39 struct SplashXPathPoint
{
43 struct SplashXPathAdjust
{
44 int firstPt
, lastPt
; // range of points
45 GBool vert
; // vertical or horizontal hint
46 SplashCoord x0a
, x0b
, // hint boundaries
49 SplashCoord x0
, x1
, xm
; // adjusted coordinates
52 //------------------------------------------------------------------------
54 // Transform a point from user space to device space.
55 inline void SplashXPath::transform(SplashCoord
*matrix
,
56 SplashCoord xi
, SplashCoord yi
,
57 SplashCoord
*xo
, SplashCoord
*yo
) {
59 // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
61 *xo
= xi
* matrix
[0] + yi
* matrix
[2] + matrix
[4];
62 *yo
= xi
* matrix
[1] + yi
* matrix
[3] + matrix
[5];
65 //------------------------------------------------------------------------
67 //------------------------------------------------------------------------
69 SplashXPath::SplashXPath(SplashPath
*path
, SplashCoord
*matrix
,
70 SplashCoord flatness
, GBool closeSubpaths
,
71 GBool adjustLines
, int linePosI
) {
73 SplashXPathPoint
*pts
;
74 SplashXPathAdjust
*adjusts
, *adjust
;
75 SplashCoord x0
, y0
, x1
, y1
, x2
, y2
, x3
, y3
, xsp
, ysp
;
76 SplashCoord adj0
, adj1
;
79 // transform the points
80 pts
= (SplashXPathPoint
*)gmallocn(path
->length
, sizeof(SplashXPathPoint
));
81 for (i
= 0; i
< path
->length
; ++i
) {
82 transform(matrix
, path
->pts
[i
].x
, path
->pts
[i
].y
, &pts
[i
].x
, &pts
[i
].y
);
85 // set up the stroke adjustment hints
87 adjusts
= (SplashXPathAdjust
*)gmallocn(path
->hintsLength
,
88 sizeof(SplashXPathAdjust
));
89 for (i
= 0; i
< path
->hintsLength
; ++i
) {
90 hint
= &path
->hints
[i
];
91 if (hint
->ctrl0
+ 1 >= path
->length
|| hint
->ctrl1
+ 1 >= path
->length
) {
96 x0
= pts
[hint
->ctrl0
].x
; y0
= pts
[hint
->ctrl0
].y
;
97 x1
= pts
[hint
->ctrl0
+ 1].x
; y1
= pts
[hint
->ctrl0
+ 1].y
;
98 x2
= pts
[hint
->ctrl1
].x
; y2
= pts
[hint
->ctrl1
].y
;
99 x3
= pts
[hint
->ctrl1
+ 1].x
; y3
= pts
[hint
->ctrl1
+ 1].y
;
100 if (x0
== x1
&& x2
== x3
) {
101 adjusts
[i
].vert
= gTrue
;
104 } else if (y0
== y1
&& y2
== y3
) {
105 adjusts
[i
].vert
= gFalse
;
118 adjusts
[i
].x0a
= adj0
- 0.01;
119 adjusts
[i
].x0b
= adj0
+ 0.01;
120 adjusts
[i
].xma
= (SplashCoord
)0.5 * (adj0
+ adj1
) - 0.01;
121 adjusts
[i
].xmb
= (SplashCoord
)0.5 * (adj0
+ adj1
) + 0.01;
122 adjusts
[i
].x1a
= adj1
- 0.01;
123 adjusts
[i
].x1b
= adj1
+ 0.01;
124 // rounding both edge coordinates can result in lines of
125 // different widths (e.g., adj=10.1, adj1=11.3 --> x0=10, x1=11;
126 // adj0=10.4, adj1=11.6 --> x0=10, x1=12), but it has the
127 // benefit of making adjacent strokes/fills line up without any
129 x0
= splashRound(adj0
);
130 x1
= splashRound(adj1
);
133 // the adjustment moves thin lines (clip rectangle with
134 // empty width or height) out of clip area, here we need
135 // a special adjustment:
142 adjusts
[i
].x0
= (SplashCoord
)x0
;
143 adjusts
[i
].x1
= (SplashCoord
)x1
- 0.01;
144 adjusts
[i
].xm
= (SplashCoord
)0.5 * (adjusts
[i
].x0
+ adjusts
[i
].x1
);
145 adjusts
[i
].firstPt
= hint
->firstPt
;
146 adjusts
[i
].lastPt
= hint
->lastPt
;
153 // perform stroke adjustment
155 for (i
= 0, adjust
= adjusts
; i
< path
->hintsLength
; ++i
, ++adjust
) {
156 for (j
= adjust
->firstPt
; j
<= adjust
->lastPt
; ++j
) {
157 strokeAdjust(adjust
, &pts
[j
].x
, &pts
[j
].y
);
166 x0
= y0
= xsp
= ysp
= 0; // make gcc happy
167 adj0
= adj1
= 0; // make gcc happy
170 while (i
< path
->length
) {
172 // first point in subpath - skip it
173 if (path
->flags
[i
] & splashPathFirst
) {
184 if (path
->flags
[i
] & splashPathCurve
) {
191 addCurve(x0
, y0
, x1
, y1
, x2
, y2
, x3
, y3
,
193 (path
->flags
[i
-1] & splashPathFirst
),
194 (path
->flags
[i
+2] & splashPathLast
),
196 (path
->flags
[i
-1] & splashPathFirst
) &&
197 !(path
->flags
[i
-1] & splashPathClosed
),
199 (path
->flags
[i
+2] & splashPathLast
) &&
200 !(path
->flags
[i
+2] & splashPathClosed
));
209 addSegment(x0
, y0
, x1
, y1
);
217 (path
->flags
[i
-1] & splashPathLast
) &&
218 (pts
[i
-1].x
!= pts
[curSubpath
].x
||
219 pts
[i
-1].y
!= pts
[curSubpath
].y
)) {
220 addSegment(x0
, y0
, xsp
, ysp
);
228 // Apply the stroke adjust hints to point <pt>: (*<xp>, *<yp>).
229 void SplashXPath::strokeAdjust(SplashXPathAdjust
*adjust
,
230 SplashCoord
*xp
, SplashCoord
*yp
) {
235 if (x
> adjust
->x0a
&& x
< adjust
->x0b
) {
237 } else if (x
> adjust
->xma
&& x
< adjust
->xmb
) {
239 } else if (x
> adjust
->x1a
&& x
< adjust
->x1b
) {
244 if (y
> adjust
->x0a
&& y
< adjust
->x0b
) {
246 } else if (y
> adjust
->xma
&& y
< adjust
->xmb
) {
248 } else if (y
> adjust
->x1a
&& y
< adjust
->x1b
) {
254 SplashXPath::SplashXPath(SplashXPath
*xPath
) {
255 length
= xPath
->length
;
257 segs
= (SplashXPathSeg
*)gmallocn(size
, sizeof(SplashXPathSeg
));
258 memcpy(segs
, xPath
->segs
, length
* sizeof(SplashXPathSeg
));
261 SplashXPath::~SplashXPath() {
265 // Add space for <nSegs> more segments
266 void SplashXPath::grow(int nSegs
) {
267 if (length
+ nSegs
> size
) {
271 while (size
< length
+ nSegs
) {
274 segs
= (SplashXPathSeg
*)greallocn(segs
, size
, sizeof(SplashXPathSeg
));
278 void SplashXPath::addCurve(SplashCoord x0
, SplashCoord y0
,
279 SplashCoord x1
, SplashCoord y1
,
280 SplashCoord x2
, SplashCoord y2
,
281 SplashCoord x3
, SplashCoord y3
,
282 SplashCoord flatness
,
283 GBool first
, GBool last
, GBool end0
, GBool end1
) {
284 SplashCoord
*cx
= new SplashCoord
[(splashMaxCurveSplits
+ 1) * 3];
285 SplashCoord
*cy
= new SplashCoord
[(splashMaxCurveSplits
+ 1) * 3];
286 int *cNext
= new int[splashMaxCurveSplits
+ 1];
287 SplashCoord xl0
, xl1
, xl2
, xr0
, xr1
, xr2
, xr3
, xx1
, xx2
, xh
;
288 SplashCoord yl0
, yl1
, yl2
, yr0
, yr1
, yr2
, yr3
, yy1
, yy2
, yh
;
289 SplashCoord dx
, dy
, mx
, my
, d1
, d2
, flatness2
;
293 flatness2
= flatness
;
295 flatness2
= flatness
* flatness
;
300 p2
= splashMaxCurveSplits
;
302 *(cx
+ p1
* 3 + 0) = x0
;
303 *(cx
+ p1
* 3 + 1) = x1
;
304 *(cx
+ p1
* 3 + 2) = x2
;
305 *(cx
+ p2
* 3 + 0) = x3
;
307 *(cy
+ p1
* 3 + 0) = y0
;
308 *(cy
+ p1
* 3 + 1) = y1
;
309 *(cy
+ p1
* 3 + 2) = y2
;
310 *(cy
+ p2
* 3 + 0) = y3
;
314 while (p1
< splashMaxCurveSplits
) {
316 // get the next segment
317 xl0
= *(cx
+ p1
* 3 + 0);
318 xx1
= *(cx
+ p1
* 3 + 1);
319 xx2
= *(cx
+ p1
* 3 + 2);
321 yl0
= *(cy
+ p1
* 3 + 0);
322 yy1
= *(cy
+ p1
* 3 + 1);
323 yy2
= *(cy
+ p1
* 3 + 2);
327 xr3
= *(cx
+ p2
* 3 + 0);
328 yr3
= *(cy
+ p2
* 3 + 0);
330 // compute the distances from the control points to the
331 // midpoint of the straight line (this is a bit of a hack, but
332 // it's much faster than computing the actual distances to the
334 mx
= (xl0
+ xr3
) * 0.5;
335 my
= (yl0
+ yr3
) * 0.5;
337 d1
= splashDist(xx1
, yy1
, mx
, my
);
338 d2
= splashDist(xx2
, yy2
, mx
, my
);
348 // if the curve is flat enough, or no more subdivisions are
349 // allowed, add the straight line segment
350 if (p2
- p1
== 1 || (d1
<= flatness2
&& d2
<= flatness2
)) {
351 addSegment(xl0
, yl0
, xr3
, yr3
);
354 // otherwise, subdivide the curve
356 xl1
= (xl0
+ xx1
) * 0.5;
357 yl1
= (yl0
+ yy1
) * 0.5;
358 xh
= (xx1
+ xx2
) * 0.5;
359 yh
= (yy1
+ yy2
) * 0.5;
360 xl2
= (xl1
+ xh
) * 0.5;
361 yl2
= (yl1
+ yh
) * 0.5;
362 xr2
= (xx2
+ xr3
) * 0.5;
363 yr2
= (yy2
+ yr3
) * 0.5;
364 xr1
= (xh
+ xr2
) * 0.5;
365 yr1
= (yh
+ yr2
) * 0.5;
366 xr0
= (xl2
+ xr1
) * 0.5;
367 yr0
= (yl2
+ yr1
) * 0.5;
368 // add the new subdivision points
371 *(cx
+ p1
* 3 + 1) = xl1
;
372 *(cx
+ p1
* 3 + 2) = xl2
;
374 *(cy
+ p1
* 3 + 1) = yl1
;
375 *(cy
+ p1
* 3 + 2) = yl2
;
379 *(cx
+ p3
* 3 + 0) = xr0
;
380 *(cx
+ p3
* 3 + 1) = xr1
;
381 *(cx
+ p3
* 3 + 2) = xr2
;
383 *(cy
+ p3
* 3 + 0) = yr0
;
384 *(cy
+ p3
* 3 + 1) = yr1
;
385 *(cy
+ p3
* 3 + 2) = yr2
;
396 void SplashXPath::addSegment(SplashCoord x0
, SplashCoord y0
,
397 SplashCoord x1
, SplashCoord y1
) {
399 segs
[length
].x0
= x0
;
400 segs
[length
].y0
= y0
;
401 segs
[length
].x1
= x1
;
402 segs
[length
].y1
= y1
;
403 segs
[length
].flags
= 0;
405 segs
[length
].dxdy
= segs
[length
].dydx
= 0;
406 segs
[length
].flags
|= splashXPathHoriz
;
408 segs
[length
].flags
|= splashXPathVert
;
410 } else if (x1
== x0
) {
411 segs
[length
].dxdy
= segs
[length
].dydx
= 0;
412 segs
[length
].flags
|= splashXPathVert
;
415 if (FixedPoint::divCheck(x1
- x0
, y1
- y0
, &segs
[length
].dxdy
)) {
416 segs
[length
].dydx
= (SplashCoord
)1 / segs
[length
].dxdy
;
418 segs
[length
].dxdy
= segs
[length
].dydx
= 0;
419 if (splashAbs(x1
- x0
) > splashAbs(y1
- y0
)) {
420 segs
[length
].flags
|= splashXPathHoriz
;
422 segs
[length
].flags
|= splashXPathVert
;
426 segs
[length
].dxdy
= (x1
- x0
) / (y1
- y0
);
427 segs
[length
].dydx
= (SplashCoord
)1 / segs
[length
].dxdy
;
431 segs
[length
].flags
|= splashXPathFlip
;
436 struct cmpXPathSegsFunctor
{
437 bool operator()(const SplashXPathSeg
&seg0
, const SplashXPathSeg
&seg1
) {
438 SplashCoord x0
, y0
, x1
, y1
;
440 if (seg0
.flags
& splashXPathFlip
) {
447 if (seg1
.flags
& splashXPathFlip
) {
454 return (y0
!= y1
) ? (y0
< y1
) : (x0
< x1
);
458 void SplashXPath::aaScale() {
462 for (i
= 0, seg
= segs
; i
< length
; ++i
, ++seg
) {
463 seg
->x0
*= splashAASize
;
464 seg
->y0
*= splashAASize
;
465 seg
->x1
*= splashAASize
;
466 seg
->y1
*= splashAASize
;
470 void SplashXPath::sort() {
471 std::sort(segs
, segs
+ length
, cmpXPathSegsFunctor());