Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

839 lines
23KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. const int juce_edgeTableDefaultEdgesPerLine = 32;
  22. //==============================================================================
  23. EdgeTable::EdgeTable (const Rectangle<int>& area,
  24. const Path& path, const AffineTransform& transform)
  25. : bounds (area),
  26. maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
  27. lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1),
  28. needToCheckEmptiness (true)
  29. {
  30. allocate();
  31. int* t = table;
  32. for (int i = bounds.getHeight(); --i >= 0;)
  33. {
  34. *t = 0;
  35. t += lineStrideElements;
  36. }
  37. const int leftLimit = bounds.getX() << 8;
  38. const int topLimit = bounds.getY() << 8;
  39. const int rightLimit = bounds.getRight() << 8;
  40. const int heightLimit = bounds.getHeight() << 8;
  41. PathFlatteningIterator iter (path, transform);
  42. while (iter.next())
  43. {
  44. int y1 = roundToInt (iter.y1 * 256.0f);
  45. int y2 = roundToInt (iter.y2 * 256.0f);
  46. if (y1 != y2)
  47. {
  48. y1 -= topLimit;
  49. y2 -= topLimit;
  50. const int startY = y1;
  51. int direction = -1;
  52. if (y1 > y2)
  53. {
  54. std::swap (y1, y2);
  55. direction = 1;
  56. }
  57. if (y1 < 0)
  58. y1 = 0;
  59. if (y2 > heightLimit)
  60. y2 = heightLimit;
  61. if (y1 < y2)
  62. {
  63. const double startX = 256.0f * iter.x1;
  64. const double multiplier = (iter.x2 - iter.x1) / (iter.y2 - iter.y1);
  65. const int stepSize = jlimit (1, 256, 256 / (1 + (int) std::abs (multiplier)));
  66. do
  67. {
  68. const int step = jmin (stepSize, y2 - y1, 256 - (y1 & 255));
  69. int x = roundToInt (startX + multiplier * ((y1 + (step >> 1)) - startY));
  70. if (x < leftLimit)
  71. x = leftLimit;
  72. else if (x >= rightLimit)
  73. x = rightLimit - 1;
  74. addEdgePoint (x, y1 >> 8, direction * step);
  75. y1 += step;
  76. }
  77. while (y1 < y2);
  78. }
  79. }
  80. }
  81. sanitiseLevels (path.isUsingNonZeroWinding());
  82. }
  83. EdgeTable::EdgeTable (const Rectangle<int>& rectangleToAdd)
  84. : bounds (rectangleToAdd),
  85. maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
  86. lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1),
  87. needToCheckEmptiness (true)
  88. {
  89. allocate();
  90. table[0] = 0;
  91. const int x1 = rectangleToAdd.getX() << 8;
  92. const int x2 = rectangleToAdd.getRight() << 8;
  93. int* t = table;
  94. for (int i = rectangleToAdd.getHeight(); --i >= 0;)
  95. {
  96. t[0] = 2;
  97. t[1] = x1;
  98. t[2] = 255;
  99. t[3] = x2;
  100. t[4] = 0;
  101. t += lineStrideElements;
  102. }
  103. }
  104. EdgeTable::EdgeTable (const RectangleList<int>& rectanglesToAdd)
  105. : bounds (rectanglesToAdd.getBounds()),
  106. maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
  107. lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1),
  108. needToCheckEmptiness (true)
  109. {
  110. allocate();
  111. clearLineSizes();
  112. for (auto& r : rectanglesToAdd)
  113. {
  114. const int x1 = r.getX() << 8;
  115. const int x2 = r.getRight() << 8;
  116. int y = r.getY() - bounds.getY();
  117. for (int j = r.getHeight(); --j >= 0;)
  118. addEdgePointPair (x1, x2, y++, 255);
  119. }
  120. sanitiseLevels (true);
  121. }
  122. EdgeTable::EdgeTable (const RectangleList<float>& rectanglesToAdd)
  123. : bounds (rectanglesToAdd.getBounds().getSmallestIntegerContainer()),
  124. maxEdgesPerLine (rectanglesToAdd.getNumRectangles() * 2),
  125. lineStrideElements (rectanglesToAdd.getNumRectangles() * 4 + 1),
  126. needToCheckEmptiness (true)
  127. {
  128. bounds.setHeight (bounds.getHeight() + 1);
  129. allocate();
  130. clearLineSizes();
  131. for (auto& r : rectanglesToAdd)
  132. {
  133. const int x1 = roundToInt (r.getX() * 256.0f);
  134. const int x2 = roundToInt (r.getRight() * 256.0f);
  135. const int y1 = roundToInt (r.getY() * 256.0f) - (bounds.getY() << 8);
  136. const int y2 = roundToInt (r.getBottom() * 256.0f) - (bounds.getY() << 8);
  137. if (x2 <= x1 || y2 <= y1)
  138. continue;
  139. int y = y1 >> 8;
  140. const int lastLine = y2 >> 8;
  141. if (y == lastLine)
  142. {
  143. addEdgePointPair (x1, x2, y, y2 - y1);
  144. }
  145. else
  146. {
  147. addEdgePointPair (x1, x2, y++, 255 - (y1 & 255));
  148. while (y < lastLine)
  149. addEdgePointPair (x1, x2, y++, 255);
  150. jassert (y < bounds.getHeight());
  151. addEdgePointPair (x1, x2, y, y2 & 255);
  152. }
  153. }
  154. sanitiseLevels (true);
  155. }
  156. EdgeTable::EdgeTable (const Rectangle<float>& rectangleToAdd)
  157. : bounds (Rectangle<int> ((int) std::floor (rectangleToAdd.getX()),
  158. roundToInt (rectangleToAdd.getY() * 256.0f) >> 8,
  159. 2 + (int) rectangleToAdd.getWidth(),
  160. 2 + (int) rectangleToAdd.getHeight())),
  161. maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
  162. lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1),
  163. needToCheckEmptiness (true)
  164. {
  165. jassert (! rectangleToAdd.isEmpty());
  166. allocate();
  167. table[0] = 0;
  168. const int x1 = roundToInt (rectangleToAdd.getX() * 256.0f);
  169. const int x2 = roundToInt (rectangleToAdd.getRight() * 256.0f);
  170. int y1 = roundToInt (rectangleToAdd.getY() * 256.0f) - (bounds.getY() << 8);
  171. jassert (y1 < 256);
  172. int y2 = roundToInt (rectangleToAdd.getBottom() * 256.0f) - (bounds.getY() << 8);
  173. if (x2 <= x1 || y2 <= y1)
  174. {
  175. bounds.setHeight (0);
  176. return;
  177. }
  178. int lineY = 0;
  179. int* t = table;
  180. if ((y1 >> 8) == (y2 >> 8))
  181. {
  182. t[0] = 2;
  183. t[1] = x1;
  184. t[2] = y2 - y1;
  185. t[3] = x2;
  186. t[4] = 0;
  187. ++lineY;
  188. t += lineStrideElements;
  189. }
  190. else
  191. {
  192. t[0] = 2;
  193. t[1] = x1;
  194. t[2] = 255 - (y1 & 255);
  195. t[3] = x2;
  196. t[4] = 0;
  197. ++lineY;
  198. t += lineStrideElements;
  199. while (lineY < (y2 >> 8))
  200. {
  201. t[0] = 2;
  202. t[1] = x1;
  203. t[2] = 255;
  204. t[3] = x2;
  205. t[4] = 0;
  206. ++lineY;
  207. t += lineStrideElements;
  208. }
  209. jassert (lineY < bounds.getHeight());
  210. t[0] = 2;
  211. t[1] = x1;
  212. t[2] = y2 & 255;
  213. t[3] = x2;
  214. t[4] = 0;
  215. ++lineY;
  216. t += lineStrideElements;
  217. }
  218. while (lineY < bounds.getHeight())
  219. {
  220. t[0] = 0;
  221. t += lineStrideElements;
  222. ++lineY;
  223. }
  224. }
  225. EdgeTable::EdgeTable (const EdgeTable& other)
  226. {
  227. operator= (other);
  228. }
  229. EdgeTable& EdgeTable::operator= (const EdgeTable& other)
  230. {
  231. bounds = other.bounds;
  232. maxEdgesPerLine = other.maxEdgesPerLine;
  233. lineStrideElements = other.lineStrideElements;
  234. needToCheckEmptiness = other.needToCheckEmptiness;
  235. allocate();
  236. copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight());
  237. return *this;
  238. }
  239. EdgeTable::~EdgeTable()
  240. {
  241. }
  242. //==============================================================================
  243. static size_t getEdgeTableAllocationSize (int lineStride, int height) noexcept
  244. {
  245. // (leave an extra line at the end for use as scratch space)
  246. return (size_t) (lineStride * (2 + jmax (0, height)));
  247. }
  248. void EdgeTable::allocate()
  249. {
  250. table.malloc (getEdgeTableAllocationSize (lineStrideElements, bounds.getHeight()));
  251. }
  252. void EdgeTable::clearLineSizes() noexcept
  253. {
  254. int* t = table;
  255. for (int i = bounds.getHeight(); --i >= 0;)
  256. {
  257. *t = 0;
  258. t += lineStrideElements;
  259. }
  260. }
  261. void EdgeTable::copyEdgeTableData (int* dest, const int destLineStride, const int* src, const int srcLineStride, int numLines) noexcept
  262. {
  263. while (--numLines >= 0)
  264. {
  265. memcpy (dest, src, (size_t) (src[0] * 2 + 1) * sizeof (int));
  266. src += srcLineStride;
  267. dest += destLineStride;
  268. }
  269. }
  270. void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept
  271. {
  272. // Convert the table from relative windings to absolute levels..
  273. int* lineStart = table;
  274. for (int y = bounds.getHeight(); --y >= 0;)
  275. {
  276. int num = lineStart[0];
  277. if (num > 0)
  278. {
  279. LineItem* items = reinterpret_cast<LineItem*> (lineStart + 1);
  280. LineItem* const itemsEnd = items + num;
  281. // sort the X coords
  282. std::sort (items, itemsEnd);
  283. const LineItem* src = items;
  284. int correctedNum = num;
  285. int level = 0;
  286. while (src < itemsEnd)
  287. {
  288. level += src->level;
  289. const int x = src->x;
  290. ++src;
  291. while (src < itemsEnd && src->x == x)
  292. {
  293. level += src->level;
  294. ++src;
  295. --correctedNum;
  296. }
  297. int corrected = std::abs (level);
  298. if (corrected >> 8)
  299. {
  300. if (useNonZeroWinding)
  301. {
  302. corrected = 255;
  303. }
  304. else
  305. {
  306. corrected &= 511;
  307. if (corrected >> 8)
  308. corrected = 511 - corrected;
  309. }
  310. }
  311. items->x = x;
  312. items->level = corrected;
  313. ++items;
  314. }
  315. lineStart[0] = correctedNum;
  316. (items - 1)->level = 0; // force the last level to 0, just in case something went wrong in creating the table
  317. }
  318. lineStart += lineStrideElements;
  319. }
  320. }
  321. void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine)
  322. {
  323. if (newNumEdgesPerLine != maxEdgesPerLine)
  324. {
  325. maxEdgesPerLine = newNumEdgesPerLine;
  326. jassert (bounds.getHeight() > 0);
  327. const int newLineStrideElements = maxEdgesPerLine * 2 + 1;
  328. HeapBlock<int> newTable (getEdgeTableAllocationSize (newLineStrideElements, bounds.getHeight()));
  329. copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.getHeight());
  330. table.swapWith (newTable);
  331. lineStrideElements = newLineStrideElements;
  332. }
  333. }
  334. void EdgeTable::optimiseTable()
  335. {
  336. int maxLineElements = 0;
  337. for (int i = bounds.getHeight(); --i >= 0;)
  338. maxLineElements = jmax (maxLineElements, table [i * lineStrideElements]);
  339. remapTableForNumEdges (maxLineElements);
  340. }
  341. void EdgeTable::addEdgePoint (const int x, const int y, const int winding)
  342. {
  343. jassert (y >= 0 && y < bounds.getHeight());
  344. int* line = table + lineStrideElements * y;
  345. const int numPoints = line[0];
  346. if (numPoints >= maxEdgesPerLine)
  347. {
  348. remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine);
  349. jassert (numPoints < maxEdgesPerLine);
  350. line = table + lineStrideElements * y;
  351. }
  352. line[0]++;
  353. int n = numPoints << 1;
  354. line [n + 1] = x;
  355. line [n + 2] = winding;
  356. }
  357. void EdgeTable::addEdgePointPair (int x1, int x2, int y, int winding)
  358. {
  359. jassert (y >= 0 && y < bounds.getHeight());
  360. int* line = table + lineStrideElements * y;
  361. const int numPoints = line[0];
  362. if (numPoints + 1 >= maxEdgesPerLine)
  363. {
  364. remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine);
  365. jassert (numPoints < maxEdgesPerLine);
  366. line = table + lineStrideElements * y;
  367. }
  368. line[0] = numPoints + 2;
  369. line += numPoints << 1;
  370. line[1] = x1;
  371. line[2] = winding;
  372. line[3] = x2;
  373. line[4] = -winding;
  374. }
  375. void EdgeTable::translate (float dx, const int dy) noexcept
  376. {
  377. bounds.translate ((int) std::floor (dx), dy);
  378. int* lineStart = table;
  379. const int intDx = (int) (dx * 256.0f);
  380. for (int i = bounds.getHeight(); --i >= 0;)
  381. {
  382. int* line = lineStart;
  383. lineStart += lineStrideElements;
  384. int num = *line++;
  385. while (--num >= 0)
  386. {
  387. *line += intDx;
  388. line += 2;
  389. }
  390. }
  391. }
  392. void EdgeTable::multiplyLevels (float amount)
  393. {
  394. int* lineStart = table;
  395. const int multiplier = (int) (amount * 256.0f);
  396. for (int y = 0; y < bounds.getHeight(); ++y)
  397. {
  398. int numPoints = lineStart[0];
  399. LineItem* item = reinterpret_cast<LineItem*> (lineStart + 1);
  400. lineStart += lineStrideElements;
  401. while (--numPoints > 0)
  402. {
  403. item->level = jmin (255, (item->level * multiplier) >> 8);
  404. ++item;
  405. }
  406. }
  407. }
  408. void EdgeTable::intersectWithEdgeTableLine (const int y, const int* const otherLine)
  409. {
  410. jassert (y >= 0 && y < bounds.getHeight());
  411. int* srcLine = table + lineStrideElements * y;
  412. int srcNum1 = *srcLine;
  413. if (srcNum1 == 0)
  414. return;
  415. int srcNum2 = *otherLine;
  416. if (srcNum2 == 0)
  417. {
  418. *srcLine = 0;
  419. return;
  420. }
  421. const int right = bounds.getRight() << 8;
  422. // optimise for the common case where our line lies entirely within a
  423. // single pair of points, as happens when clipping to a simple rect.
  424. if (srcNum2 == 2 && otherLine[2] >= 255)
  425. {
  426. clipEdgeTableLineToRange (srcLine, otherLine[1], jmin (right, otherLine[3]));
  427. return;
  428. }
  429. bool isUsingTempSpace = false;
  430. const int* src1 = srcLine + 1;
  431. int x1 = *src1++;
  432. const int* src2 = otherLine + 1;
  433. int x2 = *src2++;
  434. int destIndex = 0, destTotal = 0;
  435. int level1 = 0, level2 = 0;
  436. int lastX = std::numeric_limits<int>::min(), lastLevel = 0;
  437. while (srcNum1 > 0 && srcNum2 > 0)
  438. {
  439. int nextX;
  440. if (x1 <= x2)
  441. {
  442. if (x1 == x2)
  443. {
  444. level2 = *src2++;
  445. x2 = *src2++;
  446. --srcNum2;
  447. }
  448. nextX = x1;
  449. level1 = *src1++;
  450. x1 = *src1++;
  451. --srcNum1;
  452. }
  453. else
  454. {
  455. nextX = x2;
  456. level2 = *src2++;
  457. x2 = *src2++;
  458. --srcNum2;
  459. }
  460. if (nextX > lastX)
  461. {
  462. if (nextX >= right)
  463. break;
  464. lastX = nextX;
  465. const int nextLevel = (level1 * (level2 + 1)) >> 8;
  466. jassert (isPositiveAndBelow (nextLevel, 256));
  467. if (nextLevel != lastLevel)
  468. {
  469. if (destTotal >= maxEdgesPerLine)
  470. {
  471. srcLine[0] = destTotal;
  472. if (isUsingTempSpace)
  473. {
  474. const size_t tempSize = (size_t) srcNum1 * 2 * sizeof (int);
  475. int* const oldTemp = static_cast<int*> (alloca (tempSize));
  476. memcpy (oldTemp, src1, tempSize);
  477. remapTableForNumEdges (jmax (256, destTotal * 2));
  478. srcLine = table + lineStrideElements * y;
  479. int* const newTemp = table + lineStrideElements * bounds.getHeight();
  480. memcpy (newTemp, oldTemp, tempSize);
  481. src1 = newTemp;
  482. }
  483. else
  484. {
  485. remapTableForNumEdges (jmax (256, destTotal * 2));
  486. srcLine = table + lineStrideElements * y;
  487. }
  488. }
  489. ++destTotal;
  490. lastLevel = nextLevel;
  491. if (! isUsingTempSpace)
  492. {
  493. isUsingTempSpace = true;
  494. int* const temp = table + lineStrideElements * bounds.getHeight();
  495. memcpy (temp, src1, (size_t) srcNum1 * 2 * sizeof (int));
  496. src1 = temp;
  497. }
  498. srcLine[++destIndex] = nextX;
  499. srcLine[++destIndex] = nextLevel;
  500. }
  501. }
  502. }
  503. if (lastLevel > 0)
  504. {
  505. if (destTotal >= maxEdgesPerLine)
  506. {
  507. srcLine[0] = destTotal;
  508. remapTableForNumEdges (jmax (256, destTotal * 2));
  509. srcLine = table + lineStrideElements * y;
  510. }
  511. ++destTotal;
  512. srcLine[++destIndex] = right;
  513. srcLine[++destIndex] = 0;
  514. }
  515. srcLine[0] = destTotal;
  516. }
  517. void EdgeTable::clipEdgeTableLineToRange (int* dest, const int x1, const int x2) noexcept
  518. {
  519. int* lastItem = dest + (dest[0] * 2 - 1);
  520. if (x2 < lastItem[0])
  521. {
  522. if (x2 <= dest[1])
  523. {
  524. dest[0] = 0;
  525. return;
  526. }
  527. while (x2 < lastItem[-2])
  528. {
  529. --(dest[0]);
  530. lastItem -= 2;
  531. }
  532. lastItem[0] = x2;
  533. lastItem[1] = 0;
  534. }
  535. if (x1 > dest[1])
  536. {
  537. while (lastItem[0] > x1)
  538. lastItem -= 2;
  539. const int itemsRemoved = (int) (lastItem - (dest + 1)) / 2;
  540. if (itemsRemoved > 0)
  541. {
  542. dest[0] -= itemsRemoved;
  543. memmove (dest + 1, lastItem, (size_t) dest[0] * (sizeof (int) * 2));
  544. }
  545. dest[1] = x1;
  546. }
  547. }
  548. //==============================================================================
  549. void EdgeTable::clipToRectangle (const Rectangle<int>& r)
  550. {
  551. const Rectangle<int> clipped (r.getIntersection (bounds));
  552. if (clipped.isEmpty())
  553. {
  554. needToCheckEmptiness = false;
  555. bounds.setHeight (0);
  556. }
  557. else
  558. {
  559. const int top = clipped.getY() - bounds.getY();
  560. const int bottom = clipped.getBottom() - bounds.getY();
  561. if (bottom < bounds.getHeight())
  562. bounds.setHeight (bottom);
  563. for (int i = top; --i >= 0;)
  564. table [lineStrideElements * i] = 0;
  565. if (clipped.getX() > bounds.getX() || clipped.getRight() < bounds.getRight())
  566. {
  567. const int x1 = clipped.getX() << 8;
  568. const int x2 = jmin (bounds.getRight(), clipped.getRight()) << 8;
  569. int* line = table + lineStrideElements * top;
  570. for (int i = bottom - top; --i >= 0;)
  571. {
  572. if (line[0] != 0)
  573. clipEdgeTableLineToRange (line, x1, x2);
  574. line += lineStrideElements;
  575. }
  576. }
  577. needToCheckEmptiness = true;
  578. }
  579. }
  580. void EdgeTable::excludeRectangle (const Rectangle<int>& r)
  581. {
  582. const Rectangle<int> clipped (r.getIntersection (bounds));
  583. if (! clipped.isEmpty())
  584. {
  585. const int top = clipped.getY() - bounds.getY();
  586. const int bottom = clipped.getBottom() - bounds.getY();
  587. const int rectLine[] = { 4, std::numeric_limits<int>::min(), 255,
  588. clipped.getX() << 8, 0,
  589. clipped.getRight() << 8, 255,
  590. std::numeric_limits<int>::max(), 0 };
  591. for (int i = top; i < bottom; ++i)
  592. intersectWithEdgeTableLine (i, rectLine);
  593. needToCheckEmptiness = true;
  594. }
  595. }
  596. void EdgeTable::clipToEdgeTable (const EdgeTable& other)
  597. {
  598. const Rectangle<int> clipped (other.bounds.getIntersection (bounds));
  599. if (clipped.isEmpty())
  600. {
  601. needToCheckEmptiness = false;
  602. bounds.setHeight (0);
  603. }
  604. else
  605. {
  606. const int top = clipped.getY() - bounds.getY();
  607. const int bottom = clipped.getBottom() - bounds.getY();
  608. if (bottom < bounds.getHeight())
  609. bounds.setHeight (bottom);
  610. if (clipped.getRight() < bounds.getRight())
  611. bounds.setRight (clipped.getRight());
  612. for (int i = 0; i < top; ++i)
  613. table [lineStrideElements * i] = 0;
  614. const int* otherLine = other.table + other.lineStrideElements * (clipped.getY() - other.bounds.getY());
  615. for (int i = top; i < bottom; ++i)
  616. {
  617. intersectWithEdgeTableLine (i, otherLine);
  618. otherLine += other.lineStrideElements;
  619. }
  620. needToCheckEmptiness = true;
  621. }
  622. }
  623. void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels)
  624. {
  625. y -= bounds.getY();
  626. if (y < 0 || y >= bounds.getHeight())
  627. return;
  628. needToCheckEmptiness = true;
  629. if (numPixels <= 0)
  630. {
  631. table [lineStrideElements * y] = 0;
  632. return;
  633. }
  634. int* tempLine = static_cast<int*> (alloca ((size_t) (numPixels * 2 + 4) * sizeof (int)));
  635. int destIndex = 0, lastLevel = 0;
  636. while (--numPixels >= 0)
  637. {
  638. const int alpha = *mask;
  639. mask += maskStride;
  640. if (alpha != lastLevel)
  641. {
  642. tempLine[++destIndex] = (x << 8);
  643. tempLine[++destIndex] = alpha;
  644. lastLevel = alpha;
  645. }
  646. ++x;
  647. }
  648. if (lastLevel > 0)
  649. {
  650. tempLine[++destIndex] = (x << 8);
  651. tempLine[++destIndex] = 0;
  652. }
  653. tempLine[0] = destIndex >> 1;
  654. intersectWithEdgeTableLine (y, tempLine);
  655. }
  656. bool EdgeTable::isEmpty() noexcept
  657. {
  658. if (needToCheckEmptiness)
  659. {
  660. needToCheckEmptiness = false;
  661. int* t = table;
  662. for (int i = bounds.getHeight(); --i >= 0;)
  663. {
  664. if (t[0] > 1)
  665. return false;
  666. t += lineStrideElements;
  667. }
  668. bounds.setHeight (0);
  669. }
  670. return bounds.getHeight() == 0;
  671. }
  672. } // namespace juce