/src/libreoffice/vcl/source/gdi/regband.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <sal/config.h> |
21 | | |
22 | | #include <basegfx/numeric/ftools.hxx> |
23 | | #include <tools/helpers.hxx> |
24 | | #include <osl/diagnose.h> |
25 | | #include <sal/log.hxx> |
26 | | |
27 | | #include <regband.hxx> |
28 | | |
29 | | // ImplRegionBand |
30 | | |
31 | | // Each band contains all rectangles between upper and lower border. |
32 | | // For Union, Intersect, Xor and Exclude operations rectangles of |
33 | | // equal height are evaluated. The borders of the bands should always |
34 | | // be chosen such that this is possible. |
35 | | |
36 | | // If possible, rectangles within the bands are condensed. |
37 | | |
38 | | // When converting polygons all points of the polygon are registered |
39 | | // in the individual bands (for each band they are stored as |
40 | | // points in a list). After registration of these points they are |
41 | | // converted to rectangles and the points in the list are deleted. |
42 | | |
43 | | ImplRegionBand::ImplRegionBand( tools::Long nTop, tools::Long nBottom ) |
44 | 526k | { |
45 | | // save boundaries |
46 | 526k | mnYTop = nTop; |
47 | 526k | mnYBottom = nBottom; |
48 | | |
49 | | // initialize lists |
50 | 526k | mpNextBand = nullptr; |
51 | 526k | mpPrevBand = nullptr; |
52 | 526k | mpFirstSep = nullptr; |
53 | 526k | mpFirstBandPoint = nullptr; |
54 | 526k | mbTouched = false; |
55 | 526k | } |
56 | | |
57 | | ImplRegionBand::ImplRegionBand( |
58 | | const ImplRegionBand& rRegionBand, |
59 | | const bool bIgnorePoints) |
60 | 204k | { |
61 | | // copy boundaries |
62 | 204k | mnYTop = rRegionBand.mnYTop; |
63 | 204k | mnYBottom = rRegionBand.mnYBottom; |
64 | 204k | mbTouched = rRegionBand.mbTouched; |
65 | | |
66 | | // initialisation |
67 | 204k | mpNextBand = nullptr; |
68 | 204k | mpPrevBand = nullptr; |
69 | 204k | mpFirstSep = nullptr; |
70 | 204k | mpFirstBandPoint = nullptr; |
71 | | |
72 | | // copy all elements of the list with separations |
73 | 204k | ImplRegionBandSep* pNewSep; |
74 | 204k | ImplRegionBandSep* pPrevSep = nullptr; |
75 | 204k | ImplRegionBandSep* pSep = rRegionBand.mpFirstSep; |
76 | 384k | while ( pSep ) |
77 | 180k | { |
78 | | // create new and copy data |
79 | 180k | pNewSep = new ImplRegionBandSep; |
80 | 180k | pNewSep->mnXLeft = pSep->mnXLeft; |
81 | 180k | pNewSep->mnXRight = pSep->mnXRight; |
82 | 180k | pNewSep->mbRemoved = pSep->mbRemoved; |
83 | 180k | pNewSep->mpNextSep = nullptr; |
84 | 180k | if ( pSep == rRegionBand.mpFirstSep ) |
85 | 163k | mpFirstSep = pNewSep; |
86 | 17.5k | else |
87 | 17.5k | pPrevSep->mpNextSep = pNewSep; |
88 | | |
89 | 180k | pPrevSep = pNewSep; |
90 | 180k | pSep = pSep->mpNextSep; |
91 | 180k | } |
92 | | |
93 | 204k | if ( bIgnorePoints) |
94 | 204k | return; |
95 | | |
96 | | // Copy points. |
97 | 0 | ImplRegionBandPoint* pPoint = rRegionBand.mpFirstBandPoint; |
98 | 0 | ImplRegionBandPoint* pPrevPointCopy = nullptr; |
99 | 0 | while (pPoint != nullptr) |
100 | 0 | { |
101 | 0 | ImplRegionBandPoint* pPointCopy = new ImplRegionBandPoint; |
102 | 0 | pPointCopy->mpNextBandPoint = nullptr; |
103 | 0 | pPointCopy->mnX = pPoint->mnX; |
104 | 0 | pPointCopy->mnLineId = pPoint->mnLineId; |
105 | 0 | pPointCopy->mbEndPoint = pPoint->mbEndPoint; |
106 | 0 | pPointCopy->meLineType = pPoint->meLineType; |
107 | |
|
108 | 0 | if (pPrevPointCopy != nullptr) |
109 | 0 | pPrevPointCopy->mpNextBandPoint = pPointCopy; |
110 | 0 | else |
111 | 0 | mpFirstBandPoint = pPointCopy; |
112 | |
|
113 | 0 | pPrevPointCopy = pPointCopy; |
114 | 0 | pPoint = pPoint->mpNextBandPoint; |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | | ImplRegionBand::~ImplRegionBand() |
119 | 730k | { |
120 | 730k | SAL_WARN_IF( mpFirstBandPoint != nullptr, "vcl", "ImplRegionBand::~ImplRegionBand -> pointlist not empty" ); |
121 | | |
122 | | // delete elements of the list |
123 | 730k | ImplRegionBandSep* pSep = mpFirstSep; |
124 | 1.25M | while ( pSep ) |
125 | 524k | { |
126 | 524k | ImplRegionBandSep* pTempSep = pSep->mpNextSep; |
127 | 524k | delete pSep; |
128 | 524k | pSep = pTempSep; |
129 | 524k | } |
130 | | |
131 | | // delete elements of the list |
132 | 730k | ImplRegionBandPoint* pPoint = mpFirstBandPoint; |
133 | 730k | while ( pPoint ) |
134 | 0 | { |
135 | 0 | ImplRegionBandPoint* pTempPoint = pPoint->mpNextBandPoint; |
136 | 0 | delete pPoint; |
137 | 0 | pPoint = pTempPoint; |
138 | 0 | } |
139 | 730k | } |
140 | | |
141 | | // generate separations from lines and process union with existing |
142 | | // separations |
143 | | |
144 | | void ImplRegionBand::ProcessPoints() |
145 | 5.84k | { |
146 | | // check Pointlist |
147 | 5.84k | ImplRegionBandPoint* pRegionBandPoint = mpFirstBandPoint; |
148 | 21.5k | while ( pRegionBandPoint ) |
149 | 15.7k | { |
150 | | // within list? |
151 | 15.7k | if ( pRegionBandPoint->mpNextBandPoint ) |
152 | 13.0k | { |
153 | | // start/stop? |
154 | 13.0k | if ( pRegionBandPoint->mbEndPoint && pRegionBandPoint->mpNextBandPoint->mbEndPoint ) |
155 | 13.0k | { |
156 | | // same direction? -> remove next point! |
157 | 13.0k | if ( pRegionBandPoint->meLineType == pRegionBandPoint->mpNextBandPoint->meLineType ) |
158 | 7.38k | { |
159 | 7.38k | ImplRegionBandPoint* pSaveRegionBandPoint = pRegionBandPoint->mpNextBandPoint; |
160 | 7.38k | pRegionBandPoint->mpNextBandPoint = pRegionBandPoint->mpNextBandPoint->mpNextBandPoint; |
161 | 7.38k | delete pSaveRegionBandPoint; |
162 | 7.38k | } |
163 | 13.0k | } |
164 | 13.0k | } |
165 | | |
166 | | // continue with next element in the list |
167 | 15.7k | pRegionBandPoint = pRegionBandPoint->mpNextBandPoint; |
168 | 15.7k | } |
169 | | |
170 | 5.84k | pRegionBandPoint = mpFirstBandPoint; |
171 | 13.5k | while ( pRegionBandPoint && pRegionBandPoint->mpNextBandPoint ) |
172 | 7.66k | { |
173 | 7.66k | Union( pRegionBandPoint->mnX, pRegionBandPoint->mpNextBandPoint->mnX ); |
174 | | |
175 | 7.66k | ImplRegionBandPoint* pNextBandPoint = pRegionBandPoint->mpNextBandPoint->mpNextBandPoint; |
176 | | |
177 | | // remove already processed points |
178 | 7.66k | delete pRegionBandPoint->mpNextBandPoint; |
179 | 7.66k | delete pRegionBandPoint; |
180 | | |
181 | | // continue with next element in the list |
182 | 7.66k | pRegionBandPoint = pNextBandPoint; |
183 | 7.66k | } |
184 | | |
185 | | // remove last element if necessary |
186 | 5.84k | delete pRegionBandPoint; |
187 | | |
188 | | // list is now empty |
189 | 5.84k | mpFirstBandPoint = nullptr; |
190 | 5.84k | } |
191 | | |
192 | | // generate separations from lines and process union with existing |
193 | | // separations |
194 | | |
195 | | bool ImplRegionBand::InsertPoint( tools::Long nX, tools::Long nLineId, |
196 | | bool bEndPoint, LineType eLineType ) |
197 | 23.1k | { |
198 | 23.1k | if ( !mpFirstBandPoint ) |
199 | 3.95k | { |
200 | 3.95k | mpFirstBandPoint = new ImplRegionBandPoint; |
201 | 3.95k | mpFirstBandPoint->mnX = nX; |
202 | 3.95k | mpFirstBandPoint->mnLineId = nLineId; |
203 | 3.95k | mpFirstBandPoint->mbEndPoint = bEndPoint; |
204 | 3.95k | mpFirstBandPoint->meLineType = eLineType; |
205 | 3.95k | mpFirstBandPoint->mpNextBandPoint = nullptr; |
206 | 3.95k | return true; |
207 | 3.95k | } |
208 | | |
209 | | // look if line already touched the band |
210 | 19.1k | ImplRegionBandPoint* pRegionBandPoint = mpFirstBandPoint; |
211 | 19.1k | ImplRegionBandPoint* pLastTestedRegionBandPoint = nullptr; |
212 | 85.1k | while( pRegionBandPoint ) |
213 | 65.9k | { |
214 | 65.9k | if ( pRegionBandPoint->mnLineId == nLineId ) |
215 | 0 | { |
216 | 0 | if ( bEndPoint ) |
217 | 0 | { |
218 | 0 | if( !pRegionBandPoint->mbEndPoint ) |
219 | 0 | { |
220 | | // remove old band point |
221 | 0 | if( !mpFirstBandPoint->mpNextBandPoint ) |
222 | 0 | { |
223 | | // if we've only got one point => replace first point |
224 | 0 | pRegionBandPoint->mnX = nX; |
225 | 0 | pRegionBandPoint->mbEndPoint = true; |
226 | 0 | return true; |
227 | 0 | } |
228 | 0 | else |
229 | 0 | { |
230 | | // remove current point |
231 | 0 | if( !pLastTestedRegionBandPoint ) |
232 | 0 | { |
233 | | // remove and delete old first point |
234 | 0 | ImplRegionBandPoint* pSaveBandPoint = mpFirstBandPoint; |
235 | 0 | mpFirstBandPoint = mpFirstBandPoint->mpNextBandPoint; |
236 | 0 | delete pSaveBandPoint; |
237 | 0 | } |
238 | 0 | else |
239 | 0 | { |
240 | | // remove and delete current band point |
241 | 0 | pLastTestedRegionBandPoint->mpNextBandPoint = pRegionBandPoint->mpNextBandPoint; |
242 | 0 | delete pRegionBandPoint; |
243 | 0 | } |
244 | |
|
245 | 0 | break; |
246 | 0 | } |
247 | 0 | } |
248 | 0 | } |
249 | 0 | else |
250 | 0 | return false; |
251 | 0 | } |
252 | | |
253 | | // use next element |
254 | 65.9k | pLastTestedRegionBandPoint = pRegionBandPoint; |
255 | 65.9k | pRegionBandPoint = pRegionBandPoint->mpNextBandPoint; |
256 | 65.9k | } |
257 | | |
258 | | // search appropriate position and insert point into the list |
259 | 19.1k | ImplRegionBandPoint* pNewRegionBandPoint; |
260 | | |
261 | 19.1k | pRegionBandPoint = mpFirstBandPoint; |
262 | 19.1k | pLastTestedRegionBandPoint = nullptr; |
263 | 33.6k | while ( pRegionBandPoint ) |
264 | 32.6k | { |
265 | | // new point completely left? -> insert as first point |
266 | 32.6k | if ( nX <= pRegionBandPoint->mnX ) |
267 | 18.1k | { |
268 | 18.1k | pNewRegionBandPoint = new ImplRegionBandPoint; |
269 | 18.1k | pNewRegionBandPoint->mnX = nX; |
270 | 18.1k | pNewRegionBandPoint->mnLineId = nLineId; |
271 | 18.1k | pNewRegionBandPoint->mbEndPoint = bEndPoint; |
272 | 18.1k | pNewRegionBandPoint->meLineType = eLineType; |
273 | 18.1k | pNewRegionBandPoint->mpNextBandPoint = pRegionBandPoint; |
274 | | |
275 | | // connections to the new point |
276 | 18.1k | if ( !pLastTestedRegionBandPoint ) |
277 | 12.2k | mpFirstBandPoint = pNewRegionBandPoint; |
278 | 5.84k | else |
279 | 5.84k | pLastTestedRegionBandPoint->mpNextBandPoint = pNewRegionBandPoint; |
280 | | |
281 | 18.1k | return true; |
282 | 18.1k | } |
283 | | |
284 | | // use next element |
285 | 14.5k | pLastTestedRegionBandPoint = pRegionBandPoint; |
286 | 14.5k | pRegionBandPoint = pRegionBandPoint->mpNextBandPoint; |
287 | 14.5k | } |
288 | | |
289 | | // not inserted -> add to the end of the list |
290 | 1.06k | pNewRegionBandPoint = new ImplRegionBandPoint; |
291 | 1.06k | pNewRegionBandPoint->mnX = nX; |
292 | 1.06k | pNewRegionBandPoint->mnLineId = nLineId; |
293 | 1.06k | pNewRegionBandPoint->mbEndPoint = bEndPoint; |
294 | 1.06k | pNewRegionBandPoint->meLineType = eLineType; |
295 | 1.06k | pNewRegionBandPoint->mpNextBandPoint = nullptr; |
296 | | |
297 | | // connections to the new point |
298 | 1.06k | pLastTestedRegionBandPoint->mpNextBandPoint = pNewRegionBandPoint; |
299 | | |
300 | 1.06k | return true; |
301 | 19.1k | } |
302 | | |
303 | | void ImplRegionBand::MoveX( tools::Long nHorzMove ) |
304 | 4.08k | { |
305 | | // move all x-separations |
306 | 4.08k | ImplRegionBandSep* pSep = mpFirstSep; |
307 | 8.27k | while ( pSep ) |
308 | 4.18k | { |
309 | 4.18k | pSep->mnXLeft += nHorzMove; |
310 | 4.18k | pSep->mnXRight += nHorzMove; |
311 | 4.18k | pSep = pSep->mpNextSep; |
312 | 4.18k | } |
313 | 4.08k | } |
314 | | |
315 | | void ImplRegionBand::ScaleX( double fHorzScale ) |
316 | 0 | { |
317 | 0 | ImplRegionBandSep* pSep = mpFirstSep; |
318 | 0 | while ( pSep ) |
319 | 0 | { |
320 | 0 | pSep->mnXLeft = basegfx::fround<tools::Long>(pSep->mnXLeft * fHorzScale); |
321 | 0 | pSep->mnXRight = basegfx::fround<tools::Long>(pSep->mnXRight * fHorzScale); |
322 | 0 | pSep = pSep->mpNextSep; |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | // combine overlapping separations |
327 | | |
328 | | void ImplRegionBand::OptimizeBand() |
329 | 80.1k | { |
330 | 80.1k | ImplRegionBandSep* pPrevSep = nullptr; |
331 | 80.1k | ImplRegionBandSep* pSep = mpFirstSep; |
332 | 183k | while ( pSep ) |
333 | 103k | { |
334 | | // remove? |
335 | 103k | if ( pSep->mbRemoved || (pSep->mnXRight < pSep->mnXLeft) ) |
336 | 16.9k | { |
337 | 16.9k | ImplRegionBandSep* pOldSep = pSep; |
338 | 16.9k | if ( pSep == mpFirstSep ) |
339 | 12.4k | mpFirstSep = pSep->mpNextSep; |
340 | 4.53k | else |
341 | 4.53k | pPrevSep->mpNextSep = pSep->mpNextSep; |
342 | 16.9k | pSep = pSep->mpNextSep; |
343 | 16.9k | delete pOldSep; |
344 | 16.9k | continue; |
345 | 16.9k | } |
346 | | |
347 | | // overlapping separations? -> combine! |
348 | 86.4k | if ( pSep->mpNextSep ) |
349 | 17.0k | { |
350 | 17.0k | if ( (pSep->mnXRight+1) >= pSep->mpNextSep->mnXLeft ) |
351 | 2.84k | { |
352 | 2.84k | if ( pSep->mpNextSep->mnXRight > pSep->mnXRight ) |
353 | 2.01k | pSep->mnXRight = pSep->mpNextSep->mnXRight; |
354 | | |
355 | 2.84k | ImplRegionBandSep* pOldSep = pSep->mpNextSep; |
356 | 2.84k | pSep->mpNextSep = pOldSep->mpNextSep; |
357 | 2.84k | delete pOldSep; |
358 | 2.84k | continue; |
359 | 2.84k | } |
360 | 17.0k | } |
361 | | |
362 | 83.6k | pPrevSep = pSep; |
363 | 83.6k | pSep = pSep->mpNextSep; |
364 | 83.6k | } |
365 | 80.1k | } |
366 | | |
367 | | void ImplRegionBand::Union( tools::Long nXLeft, tools::Long nXRight ) |
368 | 390k | { |
369 | 390k | SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Union(): nxLeft > nXRight" ); |
370 | | |
371 | | // band empty? -> add element |
372 | 390k | if ( !mpFirstSep ) |
373 | 349k | { |
374 | 349k | mpFirstSep = new ImplRegionBandSep; |
375 | 349k | mpFirstSep->mnXLeft = nXLeft; |
376 | 349k | mpFirstSep->mnXRight = nXRight; |
377 | 349k | mpFirstSep->mbRemoved = false; |
378 | 349k | mpFirstSep->mpNextSep = nullptr; |
379 | 349k | return; |
380 | 349k | } |
381 | | |
382 | | // process real union |
383 | 40.7k | ImplRegionBandSep* pNewSep; |
384 | 40.7k | ImplRegionBandSep* pPrevSep = nullptr; |
385 | 40.7k | ImplRegionBandSep* pSep = mpFirstSep; |
386 | 45.8k | while ( pSep ) |
387 | 44.8k | { |
388 | | // new separation completely inside? nothing to do! |
389 | 44.8k | if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) ) |
390 | 20.1k | return; |
391 | | |
392 | | // new separation completely left? -> new separation! |
393 | 24.6k | if ( nXRight < pSep->mnXLeft ) |
394 | 5.72k | { |
395 | 5.72k | pNewSep = new ImplRegionBandSep; |
396 | 5.72k | pNewSep->mnXLeft = nXLeft; |
397 | 5.72k | pNewSep->mnXRight = nXRight; |
398 | 5.72k | pNewSep->mbRemoved = false; |
399 | | |
400 | 5.72k | pNewSep->mpNextSep = pSep; |
401 | 5.72k | if ( pSep == mpFirstSep ) |
402 | 4.89k | mpFirstSep = pNewSep; |
403 | 825 | else |
404 | 825 | pPrevSep->mpNextSep = pNewSep; |
405 | 5.72k | break; |
406 | 5.72k | } |
407 | | |
408 | | // new separation overlapping from left? -> extend boundary |
409 | 18.9k | if ( (nXRight >= pSep->mnXLeft) && (nXLeft <= pSep->mnXLeft) ) |
410 | 6.01k | pSep->mnXLeft = nXLeft; |
411 | | |
412 | | // new separation overlapping from right? -> extend boundary |
413 | 18.9k | if ( (nXLeft <= pSep->mnXRight) && (nXRight > pSep->mnXRight) ) |
414 | 5.90k | { |
415 | 5.90k | pSep->mnXRight = nXRight; |
416 | 5.90k | break; |
417 | 5.90k | } |
418 | | |
419 | | // not inserted, but last element? -> add to the end of the list |
420 | 13.0k | if ( !pSep->mpNextSep && (nXLeft > pSep->mnXRight) ) |
421 | 7.88k | { |
422 | 7.88k | pNewSep = new ImplRegionBandSep; |
423 | 7.88k | pNewSep->mnXLeft = nXLeft; |
424 | 7.88k | pNewSep->mnXRight = nXRight; |
425 | 7.88k | pNewSep->mbRemoved = false; |
426 | | |
427 | 7.88k | pSep->mpNextSep = pNewSep; |
428 | 7.88k | pNewSep->mpNextSep = nullptr; |
429 | 7.88k | break; |
430 | 7.88k | } |
431 | | |
432 | 5.13k | pPrevSep = pSep; |
433 | 5.13k | pSep = pSep->mpNextSep; |
434 | 5.13k | } |
435 | | |
436 | 20.5k | OptimizeBand(); |
437 | 20.5k | } |
438 | | |
439 | | void ImplRegionBand::Intersect( tools::Long nXLeft, tools::Long nXRight ) |
440 | 73.6k | { |
441 | 73.6k | SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Intersect(): nxLeft > nXRight" ); |
442 | | |
443 | | // band has been touched |
444 | 73.6k | mbTouched = true; |
445 | | |
446 | | // band empty? -> nothing to do |
447 | 73.6k | if ( !mpFirstSep ) |
448 | 29.3k | return; |
449 | | |
450 | | // process real intersection |
451 | 44.2k | ImplRegionBandSep* pSep = mpFirstSep; |
452 | 92.0k | while ( pSep ) |
453 | 47.7k | { |
454 | | // new separation completely outside? -> remove separation |
455 | 47.7k | if ( (nXRight < pSep->mnXLeft) || (nXLeft > pSep->mnXRight) ) |
456 | | // will be removed from the optimizer |
457 | 7.30k | pSep->mbRemoved = true; |
458 | | |
459 | | // new separation overlapping from left? -> reduce right boundary |
460 | 47.7k | if ( (nXLeft <= pSep->mnXLeft) && |
461 | 40.3k | (nXRight <= pSep->mnXRight) && |
462 | 28.8k | (nXRight >= pSep->mnXLeft) ) |
463 | 23.9k | pSep->mnXRight = nXRight; |
464 | | |
465 | | // new separation overlapping from right? -> reduce right boundary |
466 | 47.7k | if ( (nXLeft >= pSep->mnXLeft) && |
467 | 41.1k | (nXLeft <= pSep->mnXRight) && |
468 | 38.8k | (nXRight >= pSep->mnXRight) ) |
469 | 34.1k | pSep->mnXLeft = nXLeft; |
470 | | |
471 | | // new separation within the actual one? -> reduce both boundaries |
472 | 47.7k | if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) ) |
473 | 27.7k | { |
474 | 27.7k | pSep->mnXRight = nXRight; |
475 | 27.7k | pSep->mnXLeft = nXLeft; |
476 | 27.7k | } |
477 | | |
478 | 47.7k | pSep = pSep->mpNextSep; |
479 | 47.7k | } |
480 | | |
481 | 44.2k | OptimizeBand(); |
482 | 44.2k | } |
483 | | |
484 | | void ImplRegionBand::Exclude( tools::Long nXLeft, tools::Long nXRight ) |
485 | 166k | { |
486 | 166k | SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::Exclude(): nxLeft > nXRight" ); |
487 | | |
488 | | // band has been touched |
489 | 166k | mbTouched = true; |
490 | | |
491 | | // band empty? -> nothing to do |
492 | 166k | if ( !mpFirstSep ) |
493 | 151k | return; |
494 | | |
495 | | // process real exclusion |
496 | 15.3k | ImplRegionBandSep* pNewSep; |
497 | 15.3k | ImplRegionBandSep* pPrevSep = nullptr; |
498 | 15.3k | ImplRegionBandSep* pSep = mpFirstSep; |
499 | 32.1k | while ( pSep ) |
500 | 16.8k | { |
501 | 16.8k | bool bSepProcessed = false; |
502 | | |
503 | | // new separation completely overlapping? -> remove separation |
504 | 16.8k | if ( (nXLeft <= pSep->mnXLeft) && (nXRight >= pSep->mnXRight) ) |
505 | 1.48k | { |
506 | | // will be removed from the optimizer |
507 | 1.48k | pSep->mbRemoved = true; |
508 | 1.48k | bSepProcessed = true; |
509 | 1.48k | } |
510 | | |
511 | | // new separation overlapping from left? -> reduce boundary |
512 | 16.8k | if ( !bSepProcessed ) |
513 | 15.3k | { |
514 | 15.3k | if ( (nXRight >= pSep->mnXLeft) && (nXLeft <= pSep->mnXLeft) ) |
515 | 826 | { |
516 | 826 | pSep->mnXLeft = nXRight+1; |
517 | 826 | bSepProcessed = true; |
518 | 826 | } |
519 | 15.3k | } |
520 | | |
521 | | // new separation overlapping from right? -> reduce boundary |
522 | 16.8k | if ( !bSepProcessed ) |
523 | 14.4k | { |
524 | 14.4k | if ( (nXLeft <= pSep->mnXRight) && (nXRight > pSep->mnXRight) ) |
525 | 840 | { |
526 | 840 | pSep->mnXRight = nXLeft-1; |
527 | 840 | bSepProcessed = true; |
528 | 840 | } |
529 | 14.4k | } |
530 | | |
531 | | // new separation within the actual one? -> reduce boundary |
532 | | // and add new entry for reminder |
533 | 16.8k | if ( !bSepProcessed ) |
534 | 13.6k | { |
535 | 13.6k | if ( (nXLeft >= pSep->mnXLeft) && (nXRight <= pSep->mnXRight) ) |
536 | 26 | { |
537 | 26 | pNewSep = new ImplRegionBandSep; |
538 | 26 | pNewSep->mnXLeft = pSep->mnXLeft; |
539 | 26 | pNewSep->mnXRight = nXLeft-1; |
540 | 26 | pNewSep->mbRemoved = false; |
541 | | |
542 | 26 | pSep->mnXLeft = nXRight+1; |
543 | | |
544 | | // connections from the new separation |
545 | 26 | pNewSep->mpNextSep = pSep; |
546 | | |
547 | | // connections to the new separation |
548 | 26 | if ( pSep == mpFirstSep ) |
549 | 19 | mpFirstSep = pNewSep; |
550 | 7 | else |
551 | 7 | pPrevSep->mpNextSep = pNewSep; |
552 | 26 | } |
553 | 13.6k | } |
554 | | |
555 | 16.8k | pPrevSep = pSep; |
556 | 16.8k | pSep = pSep->mpNextSep; |
557 | 16.8k | } |
558 | | |
559 | 15.3k | OptimizeBand(); |
560 | 15.3k | } |
561 | | |
562 | | void ImplRegionBand::XOr( tools::Long nXLeft, tools::Long nXRight ) |
563 | 0 | { |
564 | 0 | SAL_WARN_IF( nXLeft > nXRight, "vcl", "ImplRegionBand::XOr(): nxLeft > nXRight" ); |
565 | | |
566 | | // #i46602# Reworked rectangle Xor |
567 | | |
568 | | // In general, we can distinguish 11 cases of intersection |
569 | | // (details below). The old implementation explicitly handled 7 |
570 | | // cases (numbered in the order of appearance, use CVS to get your |
571 | | // hands on the old version), therefore, I've sticked to that |
572 | | // order, and added four more cases. The code below references |
573 | | // those numbers via #1, #2, etc. |
574 | | |
575 | | // Num Mnem newX:oldX newY:oldY Description Result Can quit? |
576 | | |
577 | | // #1 Empty band - - The band is empty, thus, simply add new bandSep just add Yes |
578 | | |
579 | | // #2 apart - - The rectangles are disjunct, add new one as is just add Yes |
580 | | |
581 | | // #3 atop == == The rectangles are _exactly_ the same, remove existing just remove Yes |
582 | | |
583 | | // #4 around < > The new rectangle extends the old to both sides intersect No |
584 | | |
585 | | // #5 left < < The new rectangle is left of the old (but intersects) intersect Yes |
586 | | |
587 | | // #5b left-atop < == The new is left of the old, and coincides on the right intersect Yes |
588 | | |
589 | | // #6 right > > The new is right of the old (but intersects) intersect No |
590 | | |
591 | | // #6b right-atop == > The new is right of the old, and coincides on the left intersect No |
592 | | |
593 | | // #7 inside > < The new is fully inside the old intersect Yes |
594 | | |
595 | | // #8 inside-right > == The new is fully inside the old, coincides on the right intersect Yes |
596 | | |
597 | | // #9 inside-left == < The new is fully inside the old, coincides on the left intersect Yes |
598 | | |
599 | | // Then, to correctly perform XOr, the segment that's switched off |
600 | | // (i.e. the overlapping part of the old and the new segment) must |
601 | | // be extended by one pixel value at each border: |
602 | | // 1 1 |
603 | | // 0 4 0 4 |
604 | | // 111100000001111 |
605 | | |
606 | | // Clearly, the leading band sep now goes from 0 to 3, and the |
607 | | // trailing band sep from 11 to 14. This mimics the xor look of a |
608 | | // bitmap operation. |
609 | | |
610 | | // band empty? -> add element |
611 | 0 | if ( !mpFirstSep ) |
612 | 0 | { |
613 | 0 | mpFirstSep = new ImplRegionBandSep; |
614 | 0 | mpFirstSep->mnXLeft = nXLeft; |
615 | 0 | mpFirstSep->mnXRight = nXRight; |
616 | 0 | mpFirstSep->mbRemoved = false; |
617 | 0 | mpFirstSep->mpNextSep = nullptr; |
618 | 0 | return; |
619 | 0 | } |
620 | | |
621 | | // process real xor |
622 | 0 | ImplRegionBandSep* pNewSep; |
623 | 0 | ImplRegionBandSep* pPrevSep = nullptr; |
624 | 0 | ImplRegionBandSep* pSep = mpFirstSep; |
625 | |
|
626 | 0 | while ( pSep ) |
627 | 0 | { |
628 | 0 | tools::Long nOldLeft( pSep->mnXLeft ); |
629 | 0 | tools::Long nOldRight( pSep->mnXRight ); |
630 | | |
631 | | // did the current segment actually touch the new rect? If |
632 | | // not, skip all comparisons, go on, loop and try to find |
633 | | // intersecting bandSep |
634 | 0 | if( nXLeft <= nOldRight ) |
635 | 0 | { |
636 | 0 | if( nXRight < nOldLeft ) |
637 | 0 | { |
638 | | // #2 |
639 | | |
640 | | // add _before_ current bandSep |
641 | 0 | pNewSep = new ImplRegionBandSep; |
642 | 0 | pNewSep->mnXLeft = nXLeft; |
643 | 0 | pNewSep->mnXRight = nXRight; |
644 | 0 | pNewSep->mpNextSep = pSep; |
645 | 0 | pNewSep->mbRemoved = false; |
646 | | |
647 | | // connections from the new separation |
648 | 0 | pNewSep->mpNextSep = pSep; |
649 | | |
650 | | // connections to the new separation |
651 | 0 | if ( pSep == mpFirstSep ) |
652 | 0 | mpFirstSep = pNewSep; |
653 | 0 | else |
654 | 0 | pPrevSep->mpNextSep = pNewSep; |
655 | 0 | pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop |
656 | 0 | break; |
657 | 0 | } |
658 | 0 | else if( nXLeft == nOldLeft && nXRight == nOldRight ) |
659 | 0 | { |
660 | | // #3 |
661 | 0 | pSep->mbRemoved = true; |
662 | 0 | pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop |
663 | 0 | break; |
664 | 0 | } |
665 | 0 | else if( nXLeft != nOldLeft && nXRight == nOldRight ) |
666 | 0 | { |
667 | | // # 5b, 8 |
668 | 0 | if( nXLeft < nOldLeft ) |
669 | 0 | { |
670 | 0 | nXRight = nOldLeft; // 5b |
671 | 0 | } |
672 | 0 | else |
673 | 0 | { |
674 | 0 | nXRight = nXLeft; // 8 |
675 | 0 | nXLeft = nOldLeft; |
676 | 0 | } |
677 | |
|
678 | 0 | pSep->mnXLeft = nXLeft; |
679 | 0 | pSep->mnXRight = nXRight-1; |
680 | |
|
681 | 0 | pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop |
682 | 0 | break; |
683 | 0 | } |
684 | 0 | else if( nXLeft == nOldLeft && nXRight != nOldRight ) |
685 | 0 | { |
686 | | // # 6b, 9 |
687 | |
|
688 | 0 | if( nXRight > nOldRight ) |
689 | 0 | { |
690 | 0 | nXLeft = nOldRight+1; // 6b |
691 | | |
692 | | // cannot break here, simply mark segment as removed, |
693 | | // and go on with adapted nXLeft/nXRight |
694 | 0 | pSep->mbRemoved = true; |
695 | 0 | } |
696 | 0 | else |
697 | 0 | { |
698 | 0 | pSep->mnXLeft = nXRight+1; // 9 |
699 | |
|
700 | 0 | pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop |
701 | 0 | break; |
702 | 0 | } |
703 | 0 | } |
704 | 0 | else // if( nXLeft != nOldLeft && nXRight != nOldRight ) follows automatically |
705 | 0 | { |
706 | | // #4,5,6,7 |
707 | 0 | SAL_WARN_IF( nXLeft == nOldLeft || nXRight == nOldRight, "vcl", |
708 | 0 | "ImplRegionBand::XOr(): Case 4,5,6,7 expected all coordinates to be not equal!" ); |
709 | | |
710 | | // The plain-jane check would look like this: |
711 | | |
712 | | // if( nXLeft < nOldLeft ) |
713 | | // { |
714 | | // // #4,5 |
715 | | // if( nXRight > nOldRight ) |
716 | | // { |
717 | | // // #4 |
718 | | // } |
719 | | // else |
720 | | // { |
721 | | // // #5 done! |
722 | | // } |
723 | | // } |
724 | | // else |
725 | | // { |
726 | | // // #6,7 |
727 | | // if( nXRight > nOldRight ) |
728 | | // { |
729 | | // // #6 |
730 | | // } |
731 | | // else |
732 | | // { |
733 | | // // #7 done! |
734 | | // } |
735 | | // } |
736 | | |
737 | | // but since we generally don't have to care whether |
738 | | // it's 4 or 6 (only that we must not stop processing |
739 | | // here), condensed that in such a way that only the |
740 | | // coordinates get shuffled into correct ordering. |
741 | | |
742 | 0 | if( nXLeft < nOldLeft ) |
743 | 0 | ::std::swap( nOldLeft, nXLeft ); |
744 | |
|
745 | 0 | bool bDone( false ); |
746 | |
|
747 | 0 | if( nXRight < nOldRight ) |
748 | 0 | { |
749 | 0 | ::std::swap( nOldRight, nXRight ); |
750 | 0 | bDone = true; |
751 | 0 | } |
752 | | |
753 | | // now, nOldLeft<nXLeft<=nOldRight<nXRight always |
754 | | // holds. Note that we need the nXLeft<=nOldRight here, as |
755 | | // the intersection part might be only one pixel (original |
756 | | // nXLeft==nXRight) |
757 | 0 | SAL_WARN_IF( nOldLeft==nXLeft || nXLeft>nOldRight || nOldRight>=nXRight, "vcl", |
758 | 0 | "ImplRegionBand::XOr(): Case 4,5,6,7 expected coordinates to be ordered now!" ); |
759 | | |
760 | 0 | pSep->mnXLeft = nOldLeft; |
761 | 0 | pSep->mnXRight = nXLeft-1; |
762 | |
|
763 | 0 | nXLeft = nOldRight+1; |
764 | | // nxRight is already setup correctly |
765 | |
|
766 | 0 | if( bDone ) |
767 | 0 | { |
768 | | // add behind current bandSep |
769 | 0 | pNewSep = new ImplRegionBandSep; |
770 | |
|
771 | 0 | pNewSep->mnXLeft = nXLeft; |
772 | 0 | pNewSep->mnXRight = nXRight; |
773 | 0 | pNewSep->mpNextSep = pSep->mpNextSep; |
774 | 0 | pNewSep->mbRemoved = false; |
775 | | |
776 | | // connections from the new separation |
777 | 0 | pSep->mpNextSep = pNewSep; |
778 | |
|
779 | 0 | pPrevSep = nullptr; // do not run accidentally into the "right" case when breaking the loop |
780 | 0 | break; |
781 | 0 | } |
782 | 0 | } |
783 | 0 | } |
784 | | |
785 | 0 | pPrevSep = pSep; |
786 | 0 | pSep = pSep->mpNextSep; |
787 | 0 | } |
788 | | |
789 | | // new separation completely right of existing bandSeps ? |
790 | 0 | if( pPrevSep && nXLeft >= pPrevSep->mnXRight ) |
791 | 0 | { |
792 | 0 | pNewSep = new ImplRegionBandSep; |
793 | 0 | pNewSep->mnXLeft = nXLeft; |
794 | 0 | pNewSep->mnXRight = nXRight; |
795 | 0 | pNewSep->mpNextSep = nullptr; |
796 | 0 | pNewSep->mbRemoved = false; |
797 | | |
798 | | // connections from the new separation |
799 | 0 | pPrevSep->mpNextSep = pNewSep; |
800 | 0 | } |
801 | |
|
802 | 0 | OptimizeBand(); |
803 | 0 | } |
804 | | |
805 | | bool ImplRegionBand::Contains( tools::Long nX ) |
806 | 0 | { |
807 | 0 | ImplRegionBandSep* pSep = mpFirstSep; |
808 | 0 | while ( pSep ) |
809 | 0 | { |
810 | 0 | if ( (pSep->mnXLeft <= nX) && (pSep->mnXRight >= nX) ) |
811 | 0 | return true; |
812 | | |
813 | 0 | pSep = pSep->mpNextSep; |
814 | 0 | } |
815 | | |
816 | 0 | return false; |
817 | 0 | } |
818 | | |
819 | | tools::Long ImplRegionBand::GetXLeftBoundary() const |
820 | 199k | { |
821 | 199k | SAL_WARN_IF(mpFirstSep == nullptr, "vcl", "ImplRegionBand::XLeftBoundary -> no separation in band!"); |
822 | | |
823 | 199k | return mpFirstSep ? mpFirstSep->mnXLeft : 0; |
824 | 199k | } |
825 | | |
826 | | tools::Long ImplRegionBand::GetXRightBoundary() const |
827 | 199k | { |
828 | 199k | SAL_WARN_IF( mpFirstSep == nullptr, "vcl", "ImplRegionBand::XRightBoundary -> no separation in band!" ); |
829 | 199k | if (!mpFirstSep) |
830 | 4.89k | return 0; |
831 | | // search last separation |
832 | 194k | ImplRegionBandSep* pSep = mpFirstSep; |
833 | 194k | while ( pSep->mpNextSep ) |
834 | 141 | pSep = pSep->mpNextSep; |
835 | 194k | return pSep->mnXRight; |
836 | 199k | } |
837 | | |
838 | | bool ImplRegionBand::operator==( const ImplRegionBand& rRegionBand ) const |
839 | 47.4k | { |
840 | 47.4k | ImplRegionBandSep* pOwnRectBandSep = mpFirstSep; |
841 | 47.4k | ImplRegionBandSep* pSecondRectBandSep = rRegionBand.mpFirstSep; |
842 | 76.8k | while ( pOwnRectBandSep && pSecondRectBandSep ) |
843 | 43.4k | { |
844 | | // get boundaries of current rectangle |
845 | 43.4k | tools::Long nOwnXLeft = pOwnRectBandSep->mnXLeft; |
846 | 43.4k | tools::Long nSecondXLeft = pSecondRectBandSep->mnXLeft; |
847 | 43.4k | if ( nOwnXLeft != nSecondXLeft ) |
848 | 7.97k | return false; |
849 | | |
850 | 35.4k | tools::Long nOwnXRight = pOwnRectBandSep->mnXRight; |
851 | 35.4k | tools::Long nSecondXRight = pSecondRectBandSep->mnXRight; |
852 | 35.4k | if ( nOwnXRight != nSecondXRight ) |
853 | 6.11k | return false; |
854 | | |
855 | | // get next separation from current band |
856 | 29.3k | pOwnRectBandSep = pOwnRectBandSep->mpNextSep; |
857 | | |
858 | | // get next separation from current band |
859 | 29.3k | pSecondRectBandSep = pSecondRectBandSep->mpNextSep; |
860 | 29.3k | } |
861 | | |
862 | | // different number of separations? |
863 | 33.4k | return !(pOwnRectBandSep || pSecondRectBandSep); |
864 | 47.4k | } |
865 | | |
866 | | ImplRegionBand* ImplRegionBand::SplitBand (const sal_Int32 nY) |
867 | 0 | { |
868 | 0 | OSL_ASSERT(nY>mnYTop); |
869 | 0 | OSL_ASSERT(nY<=mnYBottom); |
870 | | |
871 | | // Create a copy of the given band (we tell the constructor to copy the points together |
872 | | // with the seps.) |
873 | 0 | ImplRegionBand* pLowerBand = new ImplRegionBand(*this, false); |
874 | | |
875 | | // Adapt vertical coordinates. |
876 | 0 | mnYBottom = nY-1; |
877 | 0 | pLowerBand->mnYTop = nY; |
878 | | |
879 | | // Insert new band into list of bands. |
880 | 0 | pLowerBand->mpNextBand = mpNextBand; |
881 | 0 | mpNextBand = pLowerBand; |
882 | 0 | pLowerBand->mpPrevBand = this; |
883 | 0 | if (pLowerBand->mpNextBand != nullptr) |
884 | 0 | pLowerBand->mpNextBand->mpPrevBand = pLowerBand; |
885 | |
|
886 | 0 | return pLowerBand; |
887 | 0 | } |
888 | | |
889 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |