1 | //======================================================================== |
2 | // |
3 | // SplashClip.cc |
4 | // |
5 | //======================================================================== |
6 | |
7 | //======================================================================== |
8 | // |
9 | // Modified under the Poppler project - http://poppler.freedesktop.org |
10 | // |
11 | // All changes made under the Poppler project to this file are licensed |
12 | // under GPL version 2 or later |
13 | // |
14 | // Copyright (C) 2010, 2021 Albert Astals Cid <aacid@kde.org> |
15 | // Copyright (C) 2013, 2021 Thomas Freitag <Thomas.Freitag@alfa.de> |
16 | // Copyright (C) 2019 Stefan BrĂ¼ns <stefan.bruens@rwth-aachen.de> |
17 | // |
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 |
20 | // |
21 | //======================================================================== |
22 | |
23 | #include <config.h> |
24 | |
25 | #include <cstdlib> |
26 | #include <cstring> |
27 | #include "goo/gmem.h" |
28 | #include "SplashErrorCodes.h" |
29 | #include "SplashMath.h" |
30 | #include "SplashPath.h" |
31 | #include "SplashXPath.h" |
32 | #include "SplashXPathScanner.h" |
33 | #include "SplashBitmap.h" |
34 | #include "SplashClip.h" |
35 | |
36 | //------------------------------------------------------------------------ |
37 | // SplashClip.flags |
38 | //------------------------------------------------------------------------ |
39 | |
40 | #define splashClipEO 0x01 // use even-odd rule |
41 | |
42 | //------------------------------------------------------------------------ |
43 | // SplashClip |
44 | //------------------------------------------------------------------------ |
45 | |
46 | SplashClip::SplashClip(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, bool antialiasA) |
47 | { |
48 | antialias = antialiasA; |
49 | if (x0 < x1) { |
50 | xMin = x0; |
51 | xMax = x1; |
52 | } else { |
53 | xMin = x1; |
54 | xMax = x0; |
55 | } |
56 | if (y0 < y1) { |
57 | yMin = y0; |
58 | yMax = y1; |
59 | } else { |
60 | yMin = y1; |
61 | yMax = y0; |
62 | } |
63 | xMinI = splashFloor(x: xMin); |
64 | yMinI = splashFloor(x: yMin); |
65 | xMaxI = splashCeil(x: xMax) - 1; |
66 | yMaxI = splashCeil(x: yMax) - 1; |
67 | flags = nullptr; |
68 | length = size = 0; |
69 | } |
70 | |
71 | SplashClip::SplashClip(const SplashClip *clip) |
72 | { |
73 | int i; |
74 | |
75 | antialias = clip->antialias; |
76 | xMin = clip->xMin; |
77 | yMin = clip->yMin; |
78 | xMax = clip->xMax; |
79 | yMax = clip->yMax; |
80 | xMinI = clip->xMinI; |
81 | yMinI = clip->yMinI; |
82 | xMaxI = clip->xMaxI; |
83 | yMaxI = clip->yMaxI; |
84 | length = clip->length; |
85 | size = clip->size; |
86 | flags = (unsigned char *)gmallocn(count: size, size: sizeof(unsigned char)); |
87 | scanners = clip->scanners; |
88 | for (i = 0; i < length; ++i) { |
89 | flags[i] = clip->flags[i]; |
90 | } |
91 | } |
92 | |
93 | SplashClip::~SplashClip() |
94 | { |
95 | gfree(p: flags); |
96 | } |
97 | |
98 | void SplashClip::grow(int nPaths) |
99 | { |
100 | if (length + nPaths > size) { |
101 | if (size == 0) { |
102 | size = 32; |
103 | } |
104 | while (size < length + nPaths) { |
105 | size *= 2; |
106 | } |
107 | flags = (unsigned char *)greallocn(p: flags, count: size, size: sizeof(unsigned char)); |
108 | } |
109 | } |
110 | |
111 | void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) |
112 | { |
113 | gfree(p: flags); |
114 | flags = nullptr; |
115 | scanners = {}; |
116 | length = size = 0; |
117 | |
118 | if (x0 < x1) { |
119 | xMin = x0; |
120 | xMax = x1; |
121 | } else { |
122 | xMin = x1; |
123 | xMax = x0; |
124 | } |
125 | if (y0 < y1) { |
126 | yMin = y0; |
127 | yMax = y1; |
128 | } else { |
129 | yMin = y1; |
130 | yMax = y0; |
131 | } |
132 | xMinI = splashFloor(x: xMin); |
133 | yMinI = splashFloor(x: yMin); |
134 | xMaxI = splashCeil(x: xMax) - 1; |
135 | yMaxI = splashCeil(x: yMax) - 1; |
136 | } |
137 | |
138 | SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) |
139 | { |
140 | if (x0 < x1) { |
141 | if (x0 > xMin) { |
142 | xMin = x0; |
143 | xMinI = splashFloor(x: xMin); |
144 | } |
145 | if (x1 < xMax) { |
146 | xMax = x1; |
147 | xMaxI = splashCeil(x: xMax) - 1; |
148 | } |
149 | } else { |
150 | if (x1 > xMin) { |
151 | xMin = x1; |
152 | xMinI = splashFloor(x: xMin); |
153 | } |
154 | if (x0 < xMax) { |
155 | xMax = x0; |
156 | xMaxI = splashCeil(x: xMax) - 1; |
157 | } |
158 | } |
159 | if (y0 < y1) { |
160 | if (y0 > yMin) { |
161 | yMin = y0; |
162 | yMinI = splashFloor(x: yMin); |
163 | } |
164 | if (y1 < yMax) { |
165 | yMax = y1; |
166 | yMaxI = splashCeil(x: yMax) - 1; |
167 | } |
168 | } else { |
169 | if (y1 > yMin) { |
170 | yMin = y1; |
171 | yMinI = splashFloor(x: yMin); |
172 | } |
173 | if (y0 < yMax) { |
174 | yMax = y0; |
175 | yMaxI = splashCeil(x: yMax) - 1; |
176 | } |
177 | } |
178 | return splashOk; |
179 | } |
180 | |
181 | SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness, bool eo) |
182 | { |
183 | int yMinAA, yMaxAA; |
184 | |
185 | SplashXPath xPath(path, matrix, flatness, true); |
186 | |
187 | // check for an empty path |
188 | if (xPath.length == 0) { |
189 | xMax = xMin - 1; |
190 | yMax = yMin - 1; |
191 | xMaxI = splashCeil(x: xMax) - 1; |
192 | yMaxI = splashCeil(x: yMax) - 1; |
193 | |
194 | // check for a rectangle |
195 | } else if (xPath.length == 4 |
196 | && ((xPath.segs[0].x0 == xPath.segs[0].x1 && xPath.segs[0].x0 == xPath.segs[1].x0 && xPath.segs[0].x0 == xPath.segs[3].x1 && xPath.segs[2].x0 == xPath.segs[2].x1 && xPath.segs[2].x0 == xPath.segs[1].x1 |
197 | && xPath.segs[2].x0 == xPath.segs[3].x0 && xPath.segs[1].y0 == xPath.segs[1].y1 && xPath.segs[1].y0 == xPath.segs[0].y1 && xPath.segs[1].y0 == xPath.segs[2].y0 && xPath.segs[3].y0 == xPath.segs[3].y1 |
198 | && xPath.segs[3].y0 == xPath.segs[0].y0 && xPath.segs[3].y0 == xPath.segs[2].y1) |
199 | || (xPath.segs[0].y0 == xPath.segs[0].y1 && xPath.segs[0].y0 == xPath.segs[1].y0 && xPath.segs[0].y0 == xPath.segs[3].y1 && xPath.segs[2].y0 == xPath.segs[2].y1 && xPath.segs[2].y0 == xPath.segs[1].y1 |
200 | && xPath.segs[2].y0 == xPath.segs[3].y0 && xPath.segs[1].x0 == xPath.segs[1].x1 && xPath.segs[1].x0 == xPath.segs[0].x1 && xPath.segs[1].x0 == xPath.segs[2].x0 && xPath.segs[3].x0 == xPath.segs[3].x1 |
201 | && xPath.segs[3].x0 == xPath.segs[0].x0 && xPath.segs[3].x0 == xPath.segs[2].x1))) { |
202 | clipToRect(x0: xPath.segs[0].x0, y0: xPath.segs[0].y0, x1: xPath.segs[2].x0, y1: xPath.segs[2].y0); |
203 | |
204 | } else { |
205 | grow(nPaths: 1); |
206 | if (antialias) { |
207 | xPath.aaScale(); |
208 | } |
209 | xPath.sort(); |
210 | flags[length] = eo ? splashClipEO : 0; |
211 | if (antialias) { |
212 | yMinAA = yMinI * splashAASize; |
213 | yMaxAA = (yMaxI + 1) * splashAASize - 1; |
214 | } else { |
215 | yMinAA = yMinI; |
216 | yMaxAA = yMaxI; |
217 | } |
218 | scanners.emplace_back(args: std::make_shared<SplashXPathScanner>(args&: xPath, args&: eo, args&: yMinAA, args&: yMaxAA)); |
219 | ++length; |
220 | } |
221 | |
222 | return splashOk; |
223 | } |
224 | |
225 | SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin, int rectXMax, int rectYMax) |
226 | { |
227 | // This tests the rectangle: |
228 | // x = [rectXMin, rectXMax + 1) (note: rect coords are ints) |
229 | // y = [rectYMin, rectYMax + 1) |
230 | // against the clipping region: |
231 | // x = [xMin, xMax) (note: clipping coords are fp) |
232 | // y = [yMin, yMax) |
233 | if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin >= xMax || (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin >= yMax) { |
234 | return splashClipAllOutside; |
235 | } |
236 | if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax && (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax && length == 0) { |
237 | return splashClipAllInside; |
238 | } |
239 | return splashClipPartial; |
240 | } |
241 | |
242 | SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) |
243 | { |
244 | int i; |
245 | |
246 | // This tests the rectangle: |
247 | // x = [spanXMin, spanXMax + 1) (note: span coords are ints) |
248 | // y = [spanY, spanY + 1) |
249 | // against the clipping region: |
250 | // x = [xMin, xMax) (note: clipping coords are fp) |
251 | // y = [yMin, yMax) |
252 | if ((SplashCoord)(spanXMax + 1) <= xMin || (SplashCoord)spanXMin >= xMax || (SplashCoord)(spanY + 1) <= yMin || (SplashCoord)spanY >= yMax) { |
253 | return splashClipAllOutside; |
254 | } |
255 | if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax && (SplashCoord)spanY >= yMin && (SplashCoord)(spanY + 1) <= yMax)) { |
256 | return splashClipPartial; |
257 | } |
258 | if (antialias) { |
259 | for (i = 0; i < length; ++i) { |
260 | if (!scanners[i]->testSpan(x0: spanXMin * splashAASize, x1: spanXMax * splashAASize + (splashAASize - 1), y: spanY * splashAASize)) { |
261 | return splashClipPartial; |
262 | } |
263 | } |
264 | } else { |
265 | for (i = 0; i < length; ++i) { |
266 | if (!scanners[i]->testSpan(x0: spanXMin, x1: spanXMax, y: spanY)) { |
267 | return splashClipPartial; |
268 | } |
269 | } |
270 | } |
271 | return splashClipAllInside; |
272 | } |
273 | |
274 | void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y, bool adjustVertLine) |
275 | { |
276 | int xx0, xx1, xx, yy, i; |
277 | SplashColorPtr p; |
278 | |
279 | // zero out pixels with x < xMin |
280 | xx0 = *x0 * splashAASize; |
281 | xx1 = splashFloor(x: xMin * splashAASize); |
282 | if (xx1 > aaBuf->getWidth()) { |
283 | xx1 = aaBuf->getWidth(); |
284 | } |
285 | if (xx0 < xx1) { |
286 | xx0 &= ~7; |
287 | for (yy = 0; yy < splashAASize; ++yy) { |
288 | p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); |
289 | for (xx = xx0; xx + 7 < xx1; xx += 8) { |
290 | *p++ = 0; |
291 | } |
292 | if (xx < xx1 && !adjustVertLine) { |
293 | *p &= 0xff >> (xx1 & 7); |
294 | } |
295 | } |
296 | *x0 = splashFloor(x: xMin); |
297 | } |
298 | |
299 | // zero out pixels with x > xMax |
300 | xx0 = splashFloor(x: xMax * splashAASize) + 1; |
301 | if (xx0 < 0) { |
302 | xx0 = 0; |
303 | } |
304 | xx1 = (*x1 + 1) * splashAASize; |
305 | if (xx0 < xx1 && !adjustVertLine) { |
306 | for (yy = 0; yy < splashAASize; ++yy) { |
307 | p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3); |
308 | xx = xx0; |
309 | if (xx & 7) { |
310 | *p &= 0xff00 >> (xx & 7); |
311 | xx = (xx & ~7) + 8; |
312 | ++p; |
313 | } |
314 | for (; xx < xx1; xx += 8) { |
315 | *p++ = 0; |
316 | } |
317 | } |
318 | *x1 = splashFloor(x: xMax); |
319 | } |
320 | |
321 | // check the paths |
322 | for (i = 0; i < length; ++i) { |
323 | scanners[i]->clipAALine(aaBuf, x0, x1, y); |
324 | } |
325 | if (*x0 > *x1) { |
326 | *x0 = *x1; |
327 | } |
328 | if (*x0 < 0) { |
329 | *x0 = 0; |
330 | } |
331 | if ((*x0 >> 1) >= aaBuf->getRowSize()) { |
332 | xx0 = *x0; |
333 | *x0 = (aaBuf->getRowSize() - 1) << 1; |
334 | if (xx0 & 1) { |
335 | *x0 = *x0 + 1; |
336 | } |
337 | } |
338 | if (*x1 < *x0) { |
339 | *x1 = *x0; |
340 | } |
341 | if ((*x1 >> 1) >= aaBuf->getRowSize()) { |
342 | xx0 = *x1; |
343 | *x1 = (aaBuf->getRowSize() - 1) << 1; |
344 | if (xx0 & 1) { |
345 | *x1 = *x1 + 1; |
346 | } |
347 | } |
348 | } |
349 | |
350 | bool SplashClip::testClipPaths(int x, int y) |
351 | { |
352 | if (antialias) { |
353 | x *= splashAASize; |
354 | y *= splashAASize; |
355 | } |
356 | |
357 | for (int i = 0; i < length; ++i) { |
358 | if (!scanners[i]->test(x, y)) { |
359 | return false; |
360 | } |
361 | } |
362 | |
363 | return true; |
364 | } |
365 | |