The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

838 lines
23KB

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