29JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6263 6386)
36 maxEdgesPerLine (
jmax (defaultEdgesPerLine / 2,
37 4 * (
int)
std::sqrt (path.data.size()))),
38 lineStrideElements (maxEdgesPerLine * 2 + 1)
43 for (
int i = bounds.getHeight(); --i >= 0;)
46 t += lineStrideElements;
49 auto leftLimit = scale *
static_cast<int64_t> (bounds.getX());
50 auto topLimit = scale *
static_cast<int64_t> (bounds.getY());
51 auto rightLimit = scale *
static_cast<int64_t> (bounds.getRight());
52 auto heightLimit = scale *
static_cast<int64_t> (bounds.getHeight());
58 const auto scaleIterY = [] (
auto y)
60 return static_cast<int64_t> (y * 256.0f + (y >= 0 ? 0.5f : -0.5f));
63 auto y1 = scaleIterY (iter.y1);
64 auto y2 = scaleIterY (iter.y2);
88 const double startX = 256.0f * iter.x1;
89 const double multiplier = (iter.x2 - iter.x1) / (iter.y2 - iter.y1);
90 auto stepSize =
static_cast<int64_t> (
jlimit (1, 256, 256 / (1 + (
int) std::abs (multiplier))));
94 auto step =
jmin (stepSize, y2 -
y1, 256 - (
y1 & 255));
95 auto x =
static_cast<int64_t> (startX + multiplier *
static_cast<double> ((
y1 + (step >> 1)) - startY));
96 auto clampedX =
static_cast<int> (
jlimit (leftLimit, rightLimit - 1, x));
98 addEdgePoint (clampedX,
static_cast<int> (
y1 / scale),
static_cast<int> (direction * step));
106 sanitiseLevels (path.isUsingNonZeroWinding());
110 : bounds (rectangleToAdd),
111 maxEdgesPerLine (defaultEdgesPerLine),
112 lineStrideElements (defaultEdgesPerLine * 2 + 1)
117 auto x1 = scale * rectangleToAdd.
getX();
118 auto x2 = scale * rectangleToAdd.
getRight();
121 for (
int i = rectangleToAdd.
getHeight(); --i >= 0;)
128 t += lineStrideElements;
133 : bounds (rectanglesToAdd.getBounds()),
134 maxEdgesPerLine (defaultEdgesPerLine),
135 lineStrideElements (defaultEdgesPerLine * 2 + 1),
136 needToCheckEmptiness (true)
141 for (
auto& r : rectanglesToAdd)
143 auto x1 = scale * r.getX();
144 auto x2 = scale * r.getRight();
145 auto y = r.getY() - bounds.
getY();
147 for (
int j = r.getHeight(); --j >= 0;)
148 addEdgePointPair (x1, x2, y++, 255);
151 sanitiseLevels (
true);
155 : bounds (rectanglesToAdd.getBounds().getSmallestIntegerContainer()),
156 maxEdgesPerLine (rectanglesToAdd.getNumRectangles() * 2),
157 lineStrideElements (rectanglesToAdd.getNumRectangles() * 4 + 1)
163 for (
auto& r : rectanglesToAdd)
165 auto x1 =
roundToInt ((
float) scale * r.getX());
166 auto x2 =
roundToInt ((
float) scale * r.getRight());
169 auto y2 =
roundToInt ((
float) scale * r.getBottom()) - (bounds.
getY() * scale);
171 if (x2 <= x1 || y2 <=
y1)
175 auto lastLine = y2 / scale;
179 addEdgePointPair (x1, x2, y, y2 -
y1);
183 addEdgePointPair (x1, x2, y++, 255 - (
y1 & 255));
186 addEdgePointPair (x1, x2, y++, 255);
189 addEdgePointPair (x1, x2, y, y2 & 255);
193 sanitiseLevels (
true);
197 : bounds ((
int)
std::
floor (rectangleToAdd.getX()),
198 roundToInt (rectangleToAdd.getY() * 256.0f) / scale,
199 2 + (
int) rectangleToAdd.getWidth(),
200 2 + (
int) rectangleToAdd.getHeight()),
201 maxEdgesPerLine (defaultEdgesPerLine),
202 lineStrideElements ((defaultEdgesPerLine * 2) + 1)
214 if (x2 <= x1 || y2 <=
y1)
223 if ((
y1 / scale) == (y2 / scale))
231 t += lineStrideElements;
237 t[2] = 255 - (
y1 & 255);
241 t += lineStrideElements;
243 while (lineY < (y2 / scale))
251 t += lineStrideElements;
261 t += lineStrideElements;
267 t += lineStrideElements;
279 bounds = other.bounds;
280 maxEdgesPerLine = other.maxEdgesPerLine;
281 lineStrideElements = other.lineStrideElements;
282 needToCheckEmptiness = other.needToCheckEmptiness;
285 copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.
getHeight());
294static size_t getEdgeTableAllocationSize (
int lineStride,
int height)
noexcept
297 return (
size_t) (lineStride * (2 +
jmax (0, height)));
300void EdgeTable::allocate()
302 table.
malloc (getEdgeTableAllocationSize (lineStrideElements, bounds.
getHeight()));
305void EdgeTable::clearLineSizes() noexcept
309 for (
int i = bounds.
getHeight(); --i >= 0;)
312 t += lineStrideElements;
316void EdgeTable::copyEdgeTableData (
int* dest,
int destLineStride,
const int* src,
int srcLineStride,
int numLines)
noexcept
318 while (--numLines >= 0)
320 memcpy (dest, src, (
size_t) (src[0] * 2 + 1) *
sizeof (
int));
321 src += srcLineStride;
322 dest += destLineStride;
326void EdgeTable::sanitiseLevels (
const bool useNonZeroWinding)
noexcept
329 int* lineStart = table;
331 for (
int y = bounds.getHeight(); --y >= 0;)
333 auto num = lineStart[0];
337 auto* items =
reinterpret_cast<LineItem*
> (lineStart + 1);
338 auto* itemsEnd = items + num;
344 auto correctedNum = num;
347 while (src < itemsEnd)
353 while (src < itemsEnd && src->x == x)
360 auto corrected = std::abs (level);
362 if (corrected / scale)
364 if (useNonZeroWinding)
372 if (corrected / scale)
373 corrected = 511 - corrected;
378 items->level = corrected;
382 lineStart[0] = correctedNum;
383 (items - 1)->level = 0;
386 lineStart += lineStrideElements;
390void EdgeTable::remapTableForNumEdges (
const int newNumEdgesPerLine)
392 if (newNumEdgesPerLine != maxEdgesPerLine)
394 maxEdgesPerLine = newNumEdgesPerLine;
397 auto newLineStrideElements = maxEdgesPerLine * 2 + 1;
399 HeapBlock<int> newTable (getEdgeTableAllocationSize (newLineStrideElements, bounds.
getHeight()));
401 copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.
getHeight());
404 lineStrideElements = newLineStrideElements;
408inline void EdgeTable::remapWithExtraSpace (
int numPoints)
410 remapTableForNumEdges (numPoints * 2);
411 jassert (numPoints < maxEdgesPerLine);
416 int maxLineElements = 0;
418 for (
int i = bounds.
getHeight(); --i >= 0;)
419 maxLineElements =
jmax (maxLineElements, table[i * lineStrideElements]);
421 remapTableForNumEdges (maxLineElements);
424void EdgeTable::addEdgePoint (
const int x,
const int y,
const int winding)
428 auto* line = table + lineStrideElements * y;
429 auto numPoints = line[0];
431 if (numPoints >= maxEdgesPerLine)
433 remapWithExtraSpace (numPoints);
434 line = table + lineStrideElements * y;
437 line[0] = numPoints + 1;
438 line += numPoints * 2;
443void EdgeTable::addEdgePointPair (
int x1,
int x2,
int y,
int winding)
447 auto* line = table + lineStrideElements * y;
448 auto numPoints = line[0];
450 if (numPoints + 1 >= maxEdgesPerLine)
452 remapWithExtraSpace (numPoints + 1);
453 line = table + lineStrideElements * y;
456 line[0] = numPoints + 2;
457 line += numPoints * 2;
464void EdgeTable::translate (
float dx,
int dy)
noexcept
468 int* lineStart = table;
469 auto intDx = (
int) (dx * 256.0f);
471 for (
int i = bounds.getHeight(); --i >= 0;)
473 auto* line = lineStart;
474 lineStart += lineStrideElements;
487 int* lineStart = table;
488 auto multiplier = (
int) (amount * 256.0f);
490 for (
int y = 0; y < bounds.
getHeight(); ++y)
492 auto numPoints = lineStart[0];
493 auto* item =
reinterpret_cast<LineItem*
> (lineStart + 1);
494 lineStart += lineStrideElements;
496 while (--numPoints > 0)
498 item->level =
jmin (255, (item->level * multiplier) / scale);
504void EdgeTable::intersectWithEdgeTableLine (
const int y,
const int*
const otherLine)
508 auto* srcLine = table + lineStrideElements * y;
509 auto srcNum1 = *srcLine;
514 auto srcNum2 = *otherLine;
522 auto right = bounds.
getRight() * scale;
526 if (srcNum2 == 2 && otherLine[2] >= 255)
528 clipEdgeTableLineToRange (srcLine, otherLine[1],
jmin (right, otherLine[3]));
532 bool isUsingTempSpace =
false;
534 const int* src1 = srcLine + 1;
537 const int* src2 = otherLine + 1;
540 int destIndex = 0, destTotal = 0;
541 int level1 = 0, level2 = 0;
544 while (srcNum1 > 0 && srcNum2 > 0)
577 auto nextLevel = (level1 * (level2 + 1)) / scale;
580 if (nextLevel != lastLevel)
582 if (destTotal >= maxEdgesPerLine)
584 srcLine[0] = destTotal;
586 if (isUsingTempSpace)
588 auto tempSize = (
size_t) srcNum1 * 2 *
sizeof (
int);
589 auto oldTemp =
static_cast<int*
> (alloca (tempSize));
590 memcpy (oldTemp, src1, tempSize);
592 remapTableForNumEdges (
jmax (256, destTotal * 2));
593 srcLine = table + lineStrideElements * y;
595 auto* newTemp = table + lineStrideElements * bounds.
getHeight();
596 memcpy (newTemp, oldTemp, tempSize);
601 remapTableForNumEdges (
jmax (256, destTotal * 2));
602 srcLine = table + lineStrideElements * y;
607 lastLevel = nextLevel;
609 if (! isUsingTempSpace)
611 isUsingTempSpace =
true;
612 auto* temp = table + lineStrideElements * bounds.
getHeight();
613 memcpy (temp, src1, (
size_t) srcNum1 * 2 *
sizeof (
int));
617 srcLine[++destIndex] = nextX;
618 srcLine[++destIndex] = nextLevel;
625 if (destTotal >= maxEdgesPerLine)
627 srcLine[0] = destTotal;
628 remapTableForNumEdges (
jmax (256, destTotal * 2));
629 srcLine = table + lineStrideElements * y;
633 srcLine[++destIndex] =
right;
634 srcLine[++destIndex] = 0;
637 srcLine[0] = destTotal;
640void EdgeTable::clipEdgeTableLineToRange (
int* dest,
const int x1,
const int x2)
noexcept
642 int* lastItem = dest + (dest[0] * 2 - 1);
644 if (x2 < lastItem[0])
652 while (x2 < lastItem[-2])
664 while (lastItem[0] > x1)
667 auto itemsRemoved = (
int) (lastItem - (dest + 1)) / 2;
669 if (itemsRemoved > 0)
671 dest[0] -= itemsRemoved;
672 memmove (dest + 1, lastItem, (
size_t) dest[0] * (
sizeof (
int) * 2));
681void EdgeTable::clipToRectangle (Rectangle<int> r)
683 auto clipped = r.getIntersection (bounds);
685 if (clipped.isEmpty())
687 needToCheckEmptiness =
false;
692 auto top = clipped.getY() - bounds.
getY();
693 auto bottom = clipped.getBottom() - bounds.
getY();
698 for (
int i = 0; i < top; ++i)
699 table[lineStrideElements * i] = 0;
701 if (clipped.getX() > bounds.
getX() || clipped.getRight() < bounds.
getRight())
703 auto x1 = scale * clipped.getX();
704 auto x2 = scale *
jmin (bounds.
getRight(), clipped.getRight());
705 int* line = table + lineStrideElements * top;
707 for (
int i = bottom - top; --i >= 0;)
710 clipEdgeTableLineToRange (line, x1, x2);
712 line += lineStrideElements;
716 needToCheckEmptiness =
true;
720void EdgeTable::excludeRectangle (Rectangle<int> r)
722 auto clipped = r.getIntersection (bounds);
724 if (! clipped.isEmpty())
726 auto top = clipped.getY() - bounds.
getY();
727 auto bottom = clipped.getBottom() - bounds.
getY();
730 scale * clipped.getX(), 0,
731 scale * clipped.getRight(), 255,
734 for (
int i = top; i < bottom; ++i)
735 intersectWithEdgeTableLine (i, rectLine);
737 needToCheckEmptiness =
true;
741void EdgeTable::clipToEdgeTable (
const EdgeTable& other)
743 auto clipped = other.bounds.getIntersection (bounds);
745 if (clipped.isEmpty())
747 needToCheckEmptiness =
false;
752 auto top = clipped.getY() - bounds.
getY();
753 auto bottom = clipped.getBottom() - bounds.
getY();
758 if (clipped.getRight() < bounds.
getRight())
759 bounds.
setRight (clipped.getRight());
761 for (
int i = 0; i < top; ++i)
762 table[lineStrideElements * i] = 0;
764 auto* otherLine = other.table + other.lineStrideElements * (clipped.getY() - other.bounds.getY());
766 for (
int i = top; i < bottom; ++i)
768 intersectWithEdgeTableLine (i, otherLine);
769 otherLine += other.lineStrideElements;
772 needToCheckEmptiness =
true;
776void EdgeTable::clipLineToMask (
int x,
int y,
const uint8* mask,
int maskStride,
int numPixels)
783 needToCheckEmptiness =
true;
787 table[lineStrideElements * y] = 0;
791 auto* tempLine =
static_cast<int*
> (alloca ((
size_t) (numPixels * 2 + 4) *
sizeof (
int)));
792 int destIndex = 0, lastLevel = 0;
794 while (--numPixels >= 0)
799 if (alpha != lastLevel)
801 tempLine[++destIndex] = (x * scale);
802 tempLine[++destIndex] = alpha;
811 tempLine[++destIndex] = (x * scale);
812 tempLine[++destIndex] = 0;
815 tempLine[0] = destIndex >> 1;
817 intersectWithEdgeTableLine (y, tempLine);
820bool EdgeTable::isEmpty() noexcept
822 if (needToCheckEmptiness)
824 needToCheckEmptiness =
false;
827 for (
int i = bounds.
getHeight(); --i >= 0;)
832 t += lineStrideElements;
841JUCE_END_IGNORE_WARNINGS_MSVC
A table of horizontal scan-line segments - used for rasterising Paths.
EdgeTable(Rectangle< int > clipLimits, const Path &pathToAdd, const AffineTransform &transform)
Creates an edge table containing a path.
void optimiseTable()
Reduces the amount of space the table has allocated.
EdgeTable & operator=(const EdgeTable &)
Copies from another edge table.
void multiplyLevels(float factor)
Scales all the alpha-levels in the table by the given multiplier.
void swapWith(HeapBlock< ElementType, otherBlockThrows > &other) noexcept
Swaps this object's data with the data of another HeapBlock.
void malloc(SizeType newNumElements, size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory.
Flattens a Path object into a series of straight-line sections.
A path is a sequence of lines and curves that may either form a closed shape or be open-ended.
Maintains a set of rectangles as a complex region.
Manages a rectangle and allows geometric operations to be performed on it.
ValueType getRight() const noexcept
Returns the x coordinate of the rectangle's right-hand-side.
ValueType getX() const noexcept
Returns the x coordinate of the rectangle's left-hand-side.
void setRight(ValueType newRight) noexcept
Adjusts the width so that the right-hand edge of the rectangle has this new value.
void setHeight(ValueType newHeight) noexcept
Changes the rectangle's height.
ValueType getBottom() const noexcept
Returns the y coordinate of the rectangle's bottom edge.
ValueType getY() const noexcept
Returns the y coordinate of the rectangle's top edge.
bool isEmpty() const noexcept
Returns true if the rectangle's width or height are zero or less.
ValueType getHeight() const noexcept
Returns the height of the rectangle.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.