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.

834 lines
22KB

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