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.

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