2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
30 #include "juce_EdgeTable.h"
31 #include "../geometry/juce_PathIterator.h"
32 #include "../imaging/juce_Image.h"
33 #include "../geometry/juce_RectangleList.h"
35 const int juce_edgeTableDefaultEdgesPerLine
= 32;
37 //==============================================================================
38 EdgeTable::EdgeTable (const Rectangle
<int>& bounds_
,
39 const Path
& path
, const AffineTransform
& transform
)
41 maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine
),
42 lineStrideElements ((juce_edgeTableDefaultEdgesPerLine
<< 1) + 1),
43 needToCheckEmptinesss (true)
45 table
.malloc ((bounds
.getHeight() + 1) * lineStrideElements
);
48 for (int i
= bounds
.getHeight(); --i
>= 0;)
51 t
+= lineStrideElements
;
54 const int topLimit
= bounds
.getY() << 8;
55 const int heightLimit
= bounds
.getHeight() << 8;
56 const int leftLimit
= bounds
.getX() << 8;
57 const int rightLimit
= bounds
.getRight() << 8;
59 PathFlatteningIterator
iter (path
, transform
);
63 int y1
= roundToInt (iter
.y1
* 256.0f
);
64 int y2
= roundToInt (iter
.y2
* 256.0f
);
71 const int startY
= y1
;
88 const double startX
= 256.0f
* iter
.x1
;
89 const double multiplier
= (iter
.x2
- iter
.x1
) / (iter
.y2
- iter
.y1
);
90 const int stepSize
= jlimit (1, 256, 256 / (1 + (int) std::abs (multiplier
)));
94 const int step
= jmin (stepSize
, y2
- y1
, 256 - (y1
& 255));
95 int x
= roundToInt (startX
+ multiplier
* ((y1
+ (step
>> 1)) - startY
));
99 else if (x
>= rightLimit
)
102 addEdgePoint (x
, y1
>> 8, direction
* step
);
110 sanitiseLevels (path
.isUsingNonZeroWinding());
113 EdgeTable::EdgeTable (const Rectangle
<int>& rectangleToAdd
)
114 : bounds (rectangleToAdd
),
115 maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine
),
116 lineStrideElements ((juce_edgeTableDefaultEdgesPerLine
<< 1) + 1),
117 needToCheckEmptinesss (true)
119 table
.malloc (jmax (1, bounds
.getHeight()) * lineStrideElements
);
122 const int x1
= rectangleToAdd
.getX() << 8;
123 const int x2
= rectangleToAdd
.getRight() << 8;
126 for (int i
= rectangleToAdd
.getHeight(); --i
>= 0;)
133 t
+= lineStrideElements
;
137 EdgeTable::EdgeTable (const RectangleList
& rectanglesToAdd
)
138 : bounds (rectanglesToAdd
.getBounds()),
139 maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine
),
140 lineStrideElements ((juce_edgeTableDefaultEdgesPerLine
<< 1) + 1),
141 needToCheckEmptinesss (true)
143 table
.malloc (jmax (1, bounds
.getHeight()) * lineStrideElements
);
146 for (int i
= bounds
.getHeight(); --i
>= 0;)
149 t
+= lineStrideElements
;
152 for (RectangleList::Iterator
iter (rectanglesToAdd
); iter
.next();)
154 const Rectangle
<int>* const r
= iter
.getRectangle();
156 const int x1
= r
->getX() << 8;
157 const int x2
= r
->getRight() << 8;
158 int y
= r
->getY() - bounds
.getY();
160 for (int j
= r
->getHeight(); --j
>= 0;)
162 addEdgePoint (x1
, y
, 255);
163 addEdgePoint (x2
, y
, -255);
168 sanitiseLevels (true);
171 EdgeTable::EdgeTable (const Rectangle
<float>& rectangleToAdd
)
172 : bounds (Rectangle
<int> ((int) std::floor (rectangleToAdd
.getX()),
173 roundToInt (rectangleToAdd
.getY() * 256.0f
) >> 8,
174 2 + (int) rectangleToAdd
.getWidth(),
175 2 + (int) rectangleToAdd
.getHeight())),
176 maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine
),
177 lineStrideElements ((juce_edgeTableDefaultEdgesPerLine
<< 1) + 1),
178 needToCheckEmptinesss (true)
180 jassert (! rectangleToAdd
.isEmpty());
181 table
.malloc (jmax (1, bounds
.getHeight()) * lineStrideElements
);
184 const int x1
= roundToInt (rectangleToAdd
.getX() * 256.0f
);
185 const int x2
= roundToInt (rectangleToAdd
.getRight() * 256.0f
);
187 int y1
= roundToInt (rectangleToAdd
.getY() * 256.0f
) - (bounds
.getY() << 8);
189 int y2
= roundToInt (rectangleToAdd
.getBottom() * 256.0f
) - (bounds
.getY() << 8);
191 if (x2
<= x1
|| y2
<= y1
)
193 bounds
.setHeight (0);
200 if ((y1
>> 8) == (y2
>> 8))
208 t
+= lineStrideElements
;
214 t
[2] = 255 - (y1
& 255);
218 t
+= lineStrideElements
;
220 while (lineY
< (y2
>> 8))
228 t
+= lineStrideElements
;
231 jassert (lineY
< bounds
.getHeight());
238 t
+= lineStrideElements
;
241 while (lineY
< bounds
.getHeight())
244 t
+= lineStrideElements
;
249 EdgeTable::EdgeTable (const EdgeTable
& other
)
254 EdgeTable
& EdgeTable::operator= (const EdgeTable
& other
)
256 bounds
= other
.bounds
;
257 maxEdgesPerLine
= other
.maxEdgesPerLine
;
258 lineStrideElements
= other
.lineStrideElements
;
259 needToCheckEmptinesss
= other
.needToCheckEmptinesss
;
261 table
.malloc (jmax (1, bounds
.getHeight()) * lineStrideElements
);
262 copyEdgeTableData (table
, lineStrideElements
, other
.table
, lineStrideElements
, bounds
.getHeight());
266 EdgeTable::~EdgeTable()
270 //==============================================================================
271 void EdgeTable::copyEdgeTableData (int* dest
, const int destLineStride
, const int* src
, const int srcLineStride
, int numLines
) noexcept
273 while (--numLines
>= 0)
275 memcpy (dest
, src
, (src
[0] * 2 + 1) * sizeof (int));
276 src
+= srcLineStride
;
277 dest
+= destLineStride
;
281 void EdgeTable::sanitiseLevels (const bool useNonZeroWinding
) noexcept
283 // Convert the table from relative windings to absolute levels..
284 int* lineStart
= table
;
286 for (int i
= bounds
.getHeight(); --i
>= 0;)
288 int* line
= lineStart
;
289 lineStart
+= lineStrideElements
;
297 if (useNonZeroWinding
)
303 int corrected
= abs (level
);
316 int corrected
= abs (level
);
321 corrected
= 511 - corrected
;
328 line
[2] = 0; // force the last level to 0, just in case something went wrong in creating the table
332 void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine
)
334 if (newNumEdgesPerLine
!= maxEdgesPerLine
)
336 maxEdgesPerLine
= newNumEdgesPerLine
;
338 jassert (bounds
.getHeight() > 0);
339 const int newLineStrideElements
= maxEdgesPerLine
* 2 + 1;
341 HeapBlock
<int> newTable (bounds
.getHeight() * newLineStrideElements
);
343 copyEdgeTableData (newTable
, newLineStrideElements
, table
, lineStrideElements
, bounds
.getHeight());
345 table
.swapWith (newTable
);
346 lineStrideElements
= newLineStrideElements
;
350 void EdgeTable::optimiseTable()
352 int maxLineElements
= 0;
354 for (int i
= bounds
.getHeight(); --i
>= 0;)
355 maxLineElements
= jmax (maxLineElements
, table
[i
* lineStrideElements
]);
357 remapTableForNumEdges (maxLineElements
);
360 void EdgeTable::addEdgePoint (const int x
, const int y
, const int winding
)
362 jassert (y
>= 0 && y
< bounds
.getHeight());
364 int* line
= table
+ lineStrideElements
* y
;
365 const int numPoints
= line
[0];
366 int n
= numPoints
<< 1;
372 const int cx
= line
[n
- 1];
388 if (numPoints
>= maxEdgesPerLine
)
390 remapTableForNumEdges (maxEdgesPerLine
+ juce_edgeTableDefaultEdgesPerLine
);
391 jassert (numPoints
< maxEdgesPerLine
);
392 line
= table
+ lineStrideElements
* y
;
395 memmove (line
+ (n
+ 3), line
+ (n
+ 1), sizeof (int) * ((numPoints
<< 1) - n
));
399 line
[n
+ 2] = winding
;
403 void EdgeTable::translate (float dx
, const int dy
) noexcept
405 bounds
.translate ((int) std::floor (dx
), dy
);
407 int* lineStart
= table
;
408 const int intDx
= (int) (dx
* 256.0f
);
410 for (int i
= bounds
.getHeight(); --i
>= 0;)
412 int* line
= lineStart
;
413 lineStart
+= lineStrideElements
;
424 void EdgeTable::intersectWithEdgeTableLine (const int y
, const int* otherLine
)
426 jassert (y
>= 0 && y
< bounds
.getHeight());
428 int* dest
= table
+ lineStrideElements
* y
;
432 int otherNumPoints
= *otherLine
;
433 if (otherNumPoints
== 0)
439 const int right
= bounds
.getRight() << 8;
441 // optimise for the common case where our line lies entirely within a
442 // single pair of points, as happens when clipping to a simple rect.
443 if (otherNumPoints
== 2 && otherLine
[2] >= 255)
445 clipEdgeTableLineToRange (dest
, otherLine
[1], jmin (right
, otherLine
[3]));
450 const size_t lineSizeBytes
= (dest
[0] * 2 + 1) * sizeof (int);
451 int* temp
= static_cast<int*> (alloca (lineSizeBytes
));
452 memcpy (temp
, dest
, lineSizeBytes
);
454 const int* src1
= temp
;
455 int srcNum1
= *src1
++;
458 const int* src2
= otherLine
;
459 int srcNum2
= otherNumPoints
;
462 int destIndex
= 0, destTotal
= 0;
463 int level1
= 0, level2
= 0;
464 int lastX
= std::numeric_limits
<int>::min(), lastLevel
= 0;
466 while (srcNum1
> 0 && srcNum2
> 0)
502 const int nextLevel
= (level1
* (level2
+ 1)) >> 8;
503 jassert (isPositiveAndBelow (nextLevel
, (int) 256));
505 if (nextLevel
!= lastLevel
)
507 if (destTotal
>= maxEdgesPerLine
)
510 remapTableForNumEdges (maxEdgesPerLine
+ juce_edgeTableDefaultEdgesPerLine
);
511 dest
= table
+ lineStrideElements
* y
;
515 lastLevel
= nextLevel
;
516 dest
[++destIndex
] = nextX
;
517 dest
[++destIndex
] = nextLevel
;
524 if (destTotal
>= maxEdgesPerLine
)
527 remapTableForNumEdges (maxEdgesPerLine
+ juce_edgeTableDefaultEdgesPerLine
);
528 dest
= table
+ lineStrideElements
* y
;
532 dest
[++destIndex
] = right
;
533 dest
[++destIndex
] = 0;
539 int last
= std::numeric_limits
<int>::min();
540 for (int i
= 0; i
< dest
[0]; ++i
)
542 jassert (dest
[i
* 2 + 1] > last
);
543 last
= dest
[i
* 2 + 1];
546 jassert (dest
[dest
[0] * 2] == 0);
550 void EdgeTable::clipEdgeTableLineToRange (int* dest
, const int x1
, const int x2
) noexcept
552 int* lastItem
= dest
+ (dest
[0] * 2 - 1);
554 if (x2
< lastItem
[0])
562 while (x2
< lastItem
[-2])
574 while (lastItem
[0] > x1
)
577 const int itemsRemoved
= (int) (lastItem
- (dest
+ 1)) / 2;
579 if (itemsRemoved
> 0)
581 dest
[0] -= itemsRemoved
;
582 memmove (dest
+ 1, lastItem
, dest
[0] * (sizeof (int) * 2));
590 //==============================================================================
591 void EdgeTable::clipToRectangle (const Rectangle
<int>& r
)
593 const Rectangle
<int> clipped (r
.getIntersection (bounds
));
595 if (clipped
.isEmpty())
597 needToCheckEmptinesss
= false;
598 bounds
.setHeight (0);
602 const int top
= clipped
.getY() - bounds
.getY();
603 const int bottom
= clipped
.getBottom() - bounds
.getY();
605 if (bottom
< bounds
.getHeight())
606 bounds
.setHeight (bottom
);
608 if (clipped
.getRight() < bounds
.getRight())
609 bounds
.setRight (clipped
.getRight());
611 for (int i
= top
; --i
>= 0;)
612 table
[lineStrideElements
* i
] = 0;
614 if (clipped
.getX() > bounds
.getX())
616 const int x1
= clipped
.getX() << 8;
617 const int x2
= jmin (bounds
.getRight(), clipped
.getRight()) << 8;
618 int* line
= table
+ lineStrideElements
* top
;
620 for (int i
= bottom
- top
; --i
>= 0;)
623 clipEdgeTableLineToRange (line
, x1
, x2
);
625 line
+= lineStrideElements
;
629 needToCheckEmptinesss
= true;
633 void EdgeTable::excludeRectangle (const Rectangle
<int>& r
)
635 const Rectangle
<int> clipped (r
.getIntersection (bounds
));
637 if (! clipped
.isEmpty())
639 const int top
= clipped
.getY() - bounds
.getY();
640 const int bottom
= clipped
.getBottom() - bounds
.getY();
642 //XXX optimise here by shortening the table if it fills top or bottom
644 const int rectLine
[] = { 4, std::numeric_limits
<int>::min(), 255,
645 clipped
.getX() << 8, 0,
646 clipped
.getRight() << 8, 255,
647 std::numeric_limits
<int>::max(), 0 };
649 for (int i
= top
; i
< bottom
; ++i
)
650 intersectWithEdgeTableLine (i
, rectLine
);
652 needToCheckEmptinesss
= true;
656 void EdgeTable::clipToEdgeTable (const EdgeTable
& other
)
658 const Rectangle
<int> clipped (other
.bounds
.getIntersection (bounds
));
660 if (clipped
.isEmpty())
662 needToCheckEmptinesss
= false;
663 bounds
.setHeight (0);
667 const int top
= clipped
.getY() - bounds
.getY();
668 const int bottom
= clipped
.getBottom() - bounds
.getY();
670 if (bottom
< bounds
.getHeight())
671 bounds
.setHeight (bottom
);
673 if (clipped
.getRight() < bounds
.getRight())
674 bounds
.setRight (clipped
.getRight());
677 for (i
= top
; --i
>= 0;)
678 table
[lineStrideElements
* i
] = 0;
680 const int* otherLine
= other
.table
+ other
.lineStrideElements
* (clipped
.getY() - other
.bounds
.getY());
682 for (i
= top
; i
< bottom
; ++i
)
684 intersectWithEdgeTableLine (i
, otherLine
);
685 otherLine
+= other
.lineStrideElements
;
688 needToCheckEmptinesss
= true;
692 void EdgeTable::clipLineToMask (int x
, int y
, const uint8
* mask
, int maskStride
, int numPixels
)
696 if (y
< 0 || y
>= bounds
.getHeight())
699 needToCheckEmptinesss
= true;
703 table
[lineStrideElements
* y
] = 0;
707 int* tempLine
= static_cast<int*> (alloca ((numPixels
* 2 + 4) * sizeof (int)));
708 int destIndex
= 0, lastLevel
= 0;
710 while (--numPixels
>= 0)
712 const int alpha
= *mask
;
715 if (alpha
!= lastLevel
)
717 tempLine
[++destIndex
] = (x
<< 8);
718 tempLine
[++destIndex
] = alpha
;
727 tempLine
[++destIndex
] = (x
<< 8);
728 tempLine
[++destIndex
] = 0;
731 tempLine
[0] = destIndex
>> 1;
733 intersectWithEdgeTableLine (y
, tempLine
);
736 bool EdgeTable::isEmpty() noexcept
738 if (needToCheckEmptinesss
)
740 needToCheckEmptinesss
= false;
743 for (int i
= bounds
.getHeight(); --i
>= 0;)
748 t
+= lineStrideElements
;
751 bounds
.setHeight (0);
754 return bounds
.getHeight() == 0;