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.

843 lines
22KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6255 6263 6386)
  21. EdgeTable::EdgeTable (Rectangle<int> area, const Path& path, const AffineTransform& transform)
  22. : bounds (area),
  23. // this is a very vague heuristic to make a rough guess at a good table size
  24. // for a given path, such that it's big enough to mostly avoid remapping, but also
  25. // not so big that it's wasteful for simple paths.
  26. maxEdgesPerLine (jmax (defaultEdgesPerLine / 2,
  27. 4 * (int) std::sqrt (path.data.size()))),
  28. lineStrideElements (maxEdgesPerLine * 2 + 1)
  29. {
  30. allocate();
  31. int* t = table;
  32. for (int i = bounds.getHeight(); --i >= 0;)
  33. {
  34. *t = 0;
  35. t += lineStrideElements;
  36. }
  37. auto leftLimit = scale * bounds.getX();
  38. auto topLimit = scale * bounds.getY();
  39. auto rightLimit = scale * bounds.getRight();
  40. auto heightLimit = scale * bounds.getHeight();
  41. PathFlatteningIterator iter (path, transform);
  42. while (iter.next())
  43. {
  44. auto y1 = roundToInt (iter.y1 * 256.0f);
  45. auto y2 = roundToInt (iter.y2 * 256.0f);
  46. if (y1 != y2)
  47. {
  48. y1 -= topLimit;
  49. y2 -= topLimit;
  50. auto 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. auto stepSize = jlimit (1, 256, 256 / (1 + (int) std::abs (multiplier)));
  66. do
  67. {
  68. auto step = jmin (stepSize, y2 - y1, 256 - (y1 & 255));
  69. auto 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 / scale, direction * step);
  75. y1 += step;
  76. }
  77. while (y1 < y2);
  78. }
  79. }
  80. }
  81. sanitiseLevels (path.isUsingNonZeroWinding());
  82. }
  83. EdgeTable::EdgeTable (Rectangle<int> rectangleToAdd)
  84. : bounds (rectangleToAdd),
  85. maxEdgesPerLine (defaultEdgesPerLine),
  86. lineStrideElements (defaultEdgesPerLine * 2 + 1)
  87. {
  88. allocate();
  89. table[0] = 0;
  90. auto x1 = scale * rectangleToAdd.getX();
  91. auto x2 = scale * rectangleToAdd.getRight();
  92. int* t = table;
  93. for (int i = rectangleToAdd.getHeight(); --i >= 0;)
  94. {
  95. t[0] = 2;
  96. t[1] = x1;
  97. t[2] = 255;
  98. t[3] = x2;
  99. t[4] = 0;
  100. t += lineStrideElements;
  101. }
  102. }
  103. EdgeTable::EdgeTable (const RectangleList<int>& rectanglesToAdd)
  104. : bounds (rectanglesToAdd.getBounds()),
  105. maxEdgesPerLine (defaultEdgesPerLine),
  106. lineStrideElements (defaultEdgesPerLine * 2 + 1),
  107. needToCheckEmptiness (true)
  108. {
  109. allocate();
  110. clearLineSizes();
  111. for (auto& r : rectanglesToAdd)
  112. {
  113. auto x1 = scale * r.getX();
  114. auto x2 = scale * r.getRight();
  115. auto y = r.getY() - bounds.getY();
  116. for (int j = r.getHeight(); --j >= 0;)
  117. addEdgePointPair (x1, x2, y++, 255);
  118. }
  119. sanitiseLevels (true);
  120. }
  121. EdgeTable::EdgeTable (const RectangleList<float>& rectanglesToAdd)
  122. : bounds (rectanglesToAdd.getBounds().getSmallestIntegerContainer()),
  123. maxEdgesPerLine (rectanglesToAdd.getNumRectangles() * 2),
  124. lineStrideElements (rectanglesToAdd.getNumRectangles() * 4 + 1)
  125. {
  126. bounds.setHeight (bounds.getHeight() + 1);
  127. allocate();
  128. clearLineSizes();
  129. for (auto& r : rectanglesToAdd)
  130. {
  131. auto x1 = roundToInt ((float) scale * r.getX());
  132. auto x2 = roundToInt ((float) scale * r.getRight());
  133. auto y1 = roundToInt ((float) scale * r.getY()) - (bounds.getY() * scale);
  134. auto y2 = roundToInt ((float) scale * r.getBottom()) - (bounds.getY() * scale);
  135. if (x2 <= x1 || y2 <= y1)
  136. continue;
  137. auto y = y1 / scale;
  138. auto lastLine = y2 / scale;
  139. if (y == lastLine)
  140. {
  141. addEdgePointPair (x1, x2, y, y2 - y1);
  142. }
  143. else
  144. {
  145. addEdgePointPair (x1, x2, y++, 255 - (y1 & 255));
  146. while (y < lastLine)
  147. addEdgePointPair (x1, x2, y++, 255);
  148. jassert (y < bounds.getHeight());
  149. addEdgePointPair (x1, x2, y, y2 & 255);
  150. }
  151. }
  152. sanitiseLevels (true);
  153. }
  154. EdgeTable::EdgeTable (Rectangle<float> rectangleToAdd)
  155. : bounds ((int) std::floor (rectangleToAdd.getX()),
  156. roundToInt (rectangleToAdd.getY() * 256.0f) / scale,
  157. 2 + (int) rectangleToAdd.getWidth(),
  158. 2 + (int) rectangleToAdd.getHeight()),
  159. maxEdgesPerLine (defaultEdgesPerLine),
  160. lineStrideElements ((defaultEdgesPerLine * 2) + 1)
  161. {
  162. jassert (! rectangleToAdd.isEmpty());
  163. allocate();
  164. table[0] = 0;
  165. auto x1 = roundToInt ((float) scale * rectangleToAdd.getX());
  166. auto x2 = roundToInt ((float) scale * rectangleToAdd.getRight());
  167. auto y1 = roundToInt ((float) scale * rectangleToAdd.getY()) - (bounds.getY() * scale);
  168. auto y2 = roundToInt ((float) scale * rectangleToAdd.getBottom()) - (bounds.getY() * scale);
  169. jassert (y1 < 256);
  170. if (x2 <= x1 || y2 <= y1)
  171. {
  172. bounds.setHeight (0);
  173. return;
  174. }
  175. int lineY = 0;
  176. int* t = table;
  177. if ((y1 / scale) == (y2 / scale))
  178. {
  179. t[0] = 2;
  180. t[1] = x1;
  181. t[2] = y2 - y1;
  182. t[3] = x2;
  183. t[4] = 0;
  184. ++lineY;
  185. t += lineStrideElements;
  186. }
  187. else
  188. {
  189. t[0] = 2;
  190. t[1] = x1;
  191. t[2] = 255 - (y1 & 255);
  192. t[3] = x2;
  193. t[4] = 0;
  194. ++lineY;
  195. t += lineStrideElements;
  196. while (lineY < (y2 / scale))
  197. {
  198. t[0] = 2;
  199. t[1] = x1;
  200. t[2] = 255;
  201. t[3] = x2;
  202. t[4] = 0;
  203. ++lineY;
  204. t += lineStrideElements;
  205. }
  206. jassert (lineY < bounds.getHeight());
  207. t[0] = 2;
  208. t[1] = x1;
  209. t[2] = y2 & 255;
  210. t[3] = x2;
  211. t[4] = 0;
  212. ++lineY;
  213. t += lineStrideElements;
  214. }
  215. while (lineY < bounds.getHeight())
  216. {
  217. t[0] = 0;
  218. t += lineStrideElements;
  219. ++lineY;
  220. }
  221. }
  222. EdgeTable::EdgeTable (const EdgeTable& other)
  223. {
  224. operator= (other);
  225. }
  226. EdgeTable& EdgeTable::operator= (const EdgeTable& other)
  227. {
  228. bounds = other.bounds;
  229. maxEdgesPerLine = other.maxEdgesPerLine;
  230. lineStrideElements = other.lineStrideElements;
  231. needToCheckEmptiness = other.needToCheckEmptiness;
  232. allocate();
  233. copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight());
  234. return *this;
  235. }
  236. EdgeTable::~EdgeTable()
  237. {
  238. }
  239. //==============================================================================
  240. static size_t getEdgeTableAllocationSize (int lineStride, int height) noexcept
  241. {
  242. // (leave an extra line at the end for use as scratch space)
  243. return (size_t) (lineStride * (2 + jmax (0, height)));
  244. }
  245. void EdgeTable::allocate()
  246. {
  247. table.malloc (getEdgeTableAllocationSize (lineStrideElements, bounds.getHeight()));
  248. }
  249. void EdgeTable::clearLineSizes() noexcept
  250. {
  251. int* t = table;
  252. for (int i = bounds.getHeight(); --i >= 0;)
  253. {
  254. *t = 0;
  255. t += lineStrideElements;
  256. }
  257. }
  258. void EdgeTable::copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) noexcept
  259. {
  260. while (--numLines >= 0)
  261. {
  262. memcpy (dest, src, (size_t) (src[0] * 2 + 1) * sizeof (int));
  263. src += srcLineStride;
  264. dest += destLineStride;
  265. }
  266. }
  267. void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept
  268. {
  269. // Convert the table from relative windings to absolute levels..
  270. int* lineStart = table;
  271. for (int y = bounds.getHeight(); --y >= 0;)
  272. {
  273. auto num = lineStart[0];
  274. if (num > 0)
  275. {
  276. auto* items = reinterpret_cast<LineItem*> (lineStart + 1);
  277. auto* itemsEnd = items + num;
  278. // sort the X coords
  279. std::sort (items, itemsEnd);
  280. auto* src = items;
  281. auto correctedNum = num;
  282. int level = 0;
  283. while (src < itemsEnd)
  284. {
  285. level += src->level;
  286. auto x = src->x;
  287. ++src;
  288. while (src < itemsEnd && src->x == x)
  289. {
  290. level += src->level;
  291. ++src;
  292. --correctedNum;
  293. }
  294. auto corrected = std::abs (level);
  295. if (corrected / scale)
  296. {
  297. if (useNonZeroWinding)
  298. {
  299. corrected = 255;
  300. }
  301. else
  302. {
  303. corrected &= 511;
  304. if (corrected / scale)
  305. corrected = 511 - corrected;
  306. }
  307. }
  308. items->x = x;
  309. items->level = corrected;
  310. ++items;
  311. }
  312. lineStart[0] = correctedNum;
  313. (items - 1)->level = 0; // force the last level to 0, just in case something went wrong in creating the table
  314. }
  315. lineStart += lineStrideElements;
  316. }
  317. }
  318. void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine)
  319. {
  320. if (newNumEdgesPerLine != maxEdgesPerLine)
  321. {
  322. maxEdgesPerLine = newNumEdgesPerLine;
  323. jassert (bounds.getHeight() > 0);
  324. auto newLineStrideElements = maxEdgesPerLine * 2 + 1;
  325. HeapBlock<int> newTable (getEdgeTableAllocationSize (newLineStrideElements, bounds.getHeight()));
  326. copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.getHeight());
  327. table.swapWith (newTable);
  328. lineStrideElements = newLineStrideElements;
  329. }
  330. }
  331. inline void EdgeTable::remapWithExtraSpace (int numPoints)
  332. {
  333. remapTableForNumEdges (numPoints * 2);
  334. jassert (numPoints < maxEdgesPerLine);
  335. }
  336. void EdgeTable::optimiseTable()
  337. {
  338. int maxLineElements = 0;
  339. for (int i = bounds.getHeight(); --i >= 0;)
  340. maxLineElements = jmax (maxLineElements, table[i * lineStrideElements]);
  341. remapTableForNumEdges (maxLineElements);
  342. }
  343. void EdgeTable::addEdgePoint (const int x, const int y, const int winding)
  344. {
  345. jassert (y >= 0 && y < bounds.getHeight());
  346. auto* line = table + lineStrideElements * y;
  347. auto numPoints = line[0];
  348. if (numPoints >= maxEdgesPerLine)
  349. {
  350. remapWithExtraSpace (numPoints);
  351. line = table + lineStrideElements * y;
  352. }
  353. line[0] = numPoints + 1;
  354. line += numPoints * 2;
  355. line[1] = x;
  356. line[2] = winding;
  357. }
  358. void EdgeTable::addEdgePointPair (int x1, int x2, int y, int winding)
  359. {
  360. jassert (y >= 0 && y < bounds.getHeight());
  361. auto* line = table + lineStrideElements * y;
  362. auto numPoints = line[0];
  363. if (numPoints + 1 >= maxEdgesPerLine)
  364. {
  365. remapWithExtraSpace (numPoints + 1);
  366. line = table + lineStrideElements * y;
  367. }
  368. line[0] = numPoints + 2;
  369. line += numPoints * 2;
  370. line[1] = x1;
  371. line[2] = winding;
  372. line[3] = x2;
  373. line[4] = -winding;
  374. }
  375. void EdgeTable::translate (float dx, int dy) noexcept
  376. {
  377. bounds.translate ((int) std::floor (dx), dy);
  378. int* lineStart = table;
  379. auto intDx = (int) (dx * 256.0f);
  380. for (int i = bounds.getHeight(); --i >= 0;)
  381. {
  382. auto* line = lineStart;
  383. lineStart += lineStrideElements;
  384. auto 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. auto multiplier = (int) (amount * 256.0f);
  396. for (int y = 0; y < bounds.getHeight(); ++y)
  397. {
  398. auto numPoints = lineStart[0];
  399. auto* item = reinterpret_cast<LineItem*> (lineStart + 1);
  400. lineStart += lineStrideElements;
  401. while (--numPoints > 0)
  402. {
  403. item->level = jmin (255, (item->level * multiplier) / scale);
  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. auto* srcLine = table + lineStrideElements * y;
  412. auto srcNum1 = *srcLine;
  413. if (srcNum1 == 0)
  414. return;
  415. auto srcNum2 = *otherLine;
  416. if (srcNum2 == 0)
  417. {
  418. *srcLine = 0;
  419. return;
  420. }
  421. auto right = bounds.getRight() * scale;
  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. auto x1 = *src1++;
  432. const int* src2 = otherLine + 1;
  433. auto 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. auto nextLevel = (level1 * (level2 + 1)) / scale;
  466. jassert (isPositiveAndBelow (nextLevel, 256));
  467. if (nextLevel != lastLevel)
  468. {
  469. if (destTotal >= maxEdgesPerLine)
  470. {
  471. srcLine[0] = destTotal;
  472. if (isUsingTempSpace)
  473. {
  474. auto tempSize = (size_t) srcNum1 * 2 * sizeof (int);
  475. auto oldTemp = static_cast<int*> (alloca (tempSize));
  476. memcpy (oldTemp, src1, tempSize);
  477. remapTableForNumEdges (jmax (256, destTotal * 2));
  478. srcLine = table + lineStrideElements * y;
  479. auto* 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. auto* 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. auto 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 (Rectangle<int> r)
  550. {
  551. auto clipped = r.getIntersection (bounds);
  552. if (clipped.isEmpty())
  553. {
  554. needToCheckEmptiness = false;
  555. bounds.setHeight (0);
  556. }
  557. else
  558. {
  559. auto top = clipped.getY() - bounds.getY();
  560. auto bottom = clipped.getBottom() - bounds.getY();
  561. if (bottom < bounds.getHeight())
  562. bounds.setHeight (bottom);
  563. for (int i = 0; i < top; ++i)
  564. table[lineStrideElements * i] = 0;
  565. if (clipped.getX() > bounds.getX() || clipped.getRight() < bounds.getRight())
  566. {
  567. auto x1 = scale * clipped.getX();
  568. auto x2 = scale * jmin (bounds.getRight(), clipped.getRight());
  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 (Rectangle<int> r)
  581. {
  582. auto clipped = r.getIntersection (bounds);
  583. if (! clipped.isEmpty())
  584. {
  585. auto top = clipped.getY() - bounds.getY();
  586. auto bottom = clipped.getBottom() - bounds.getY();
  587. const int rectLine[] = { 4, std::numeric_limits<int>::min(), 255,
  588. scale * clipped.getX(), 0,
  589. scale * clipped.getRight(), 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. auto clipped = other.bounds.getIntersection (bounds);
  599. if (clipped.isEmpty())
  600. {
  601. needToCheckEmptiness = false;
  602. bounds.setHeight (0);
  603. }
  604. else
  605. {
  606. auto top = clipped.getY() - bounds.getY();
  607. auto 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. auto* 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. auto* tempLine = static_cast<int*> (alloca ((size_t) (numPixels * 2 + 4) * sizeof (int)));
  635. int destIndex = 0, lastLevel = 0;
  636. while (--numPixels >= 0)
  637. {
  638. auto alpha = *mask;
  639. mask += maskStride;
  640. if (alpha != lastLevel)
  641. {
  642. tempLine[++destIndex] = (x * scale);
  643. tempLine[++destIndex] = alpha;
  644. lastLevel = alpha;
  645. }
  646. ++x;
  647. }
  648. if (lastLevel > 0)
  649. {
  650. tempLine[++destIndex] = (x * scale);
  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. JUCE_END_IGNORE_WARNINGS_MSVC
  673. } // namespace juce