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.

739 lines
20KB

  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>& bounds_,
  20. const Path& path, const AffineTransform& transform)
  21. : bounds (bounds_),
  22. maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
  23. lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1),
  24. needToCheckEmptinesss (true)
  25. {
  26. table.malloc ((size_t) ((bounds.getHeight() + 1) * lineStrideElements));
  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 << 1) + 1),
  83. needToCheckEmptinesss (true)
  84. {
  85. table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements));
  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 << 1) + 1),
  104. needToCheckEmptinesss (true)
  105. {
  106. table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements));
  107. int* t = table;
  108. for (int i = bounds.getHeight(); --i >= 0;)
  109. {
  110. *t = 0;
  111. t += lineStrideElements;
  112. }
  113. for (const Rectangle<int>* r = rectanglesToAdd.begin(), * const e = rectanglesToAdd.end(); r != e; ++r)
  114. {
  115. const int x1 = r->getX() << 8;
  116. const int x2 = r->getRight() << 8;
  117. int y = r->getY() - bounds.getY();
  118. for (int j = r->getHeight(); --j >= 0;)
  119. {
  120. addEdgePoint (x1, y, 255);
  121. addEdgePoint (x2, y, -255);
  122. ++y;
  123. }
  124. }
  125. sanitiseLevels (true);
  126. }
  127. EdgeTable::EdgeTable (const Rectangle<float>& rectangleToAdd)
  128. : bounds (Rectangle<int> ((int) std::floor (rectangleToAdd.getX()),
  129. roundToInt (rectangleToAdd.getY() * 256.0f) >> 8,
  130. 2 + (int) rectangleToAdd.getWidth(),
  131. 2 + (int) rectangleToAdd.getHeight())),
  132. maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
  133. lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1),
  134. needToCheckEmptinesss (true)
  135. {
  136. jassert (! rectangleToAdd.isEmpty());
  137. table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements));
  138. table[0] = 0;
  139. const int x1 = roundToInt (rectangleToAdd.getX() * 256.0f);
  140. const int x2 = roundToInt (rectangleToAdd.getRight() * 256.0f);
  141. int y1 = roundToInt (rectangleToAdd.getY() * 256.0f) - (bounds.getY() << 8);
  142. jassert (y1 < 256);
  143. int y2 = roundToInt (rectangleToAdd.getBottom() * 256.0f) - (bounds.getY() << 8);
  144. if (x2 <= x1 || y2 <= y1)
  145. {
  146. bounds.setHeight (0);
  147. return;
  148. }
  149. int lineY = 0;
  150. int* t = table;
  151. if ((y1 >> 8) == (y2 >> 8))
  152. {
  153. t[0] = 2;
  154. t[1] = x1;
  155. t[2] = y2 - y1;
  156. t[3] = x2;
  157. t[4] = 0;
  158. ++lineY;
  159. t += lineStrideElements;
  160. }
  161. else
  162. {
  163. t[0] = 2;
  164. t[1] = x1;
  165. t[2] = 255 - (y1 & 255);
  166. t[3] = x2;
  167. t[4] = 0;
  168. ++lineY;
  169. t += lineStrideElements;
  170. while (lineY < (y2 >> 8))
  171. {
  172. t[0] = 2;
  173. t[1] = x1;
  174. t[2] = 255;
  175. t[3] = x2;
  176. t[4] = 0;
  177. ++lineY;
  178. t += lineStrideElements;
  179. }
  180. jassert (lineY < bounds.getHeight());
  181. t[0] = 2;
  182. t[1] = x1;
  183. t[2] = y2 & 255;
  184. t[3] = x2;
  185. t[4] = 0;
  186. ++lineY;
  187. t += lineStrideElements;
  188. }
  189. while (lineY < bounds.getHeight())
  190. {
  191. t[0] = 0;
  192. t += lineStrideElements;
  193. ++lineY;
  194. }
  195. }
  196. EdgeTable::EdgeTable (const EdgeTable& other)
  197. {
  198. operator= (other);
  199. }
  200. EdgeTable& EdgeTable::operator= (const EdgeTable& other)
  201. {
  202. bounds = other.bounds;
  203. maxEdgesPerLine = other.maxEdgesPerLine;
  204. lineStrideElements = other.lineStrideElements;
  205. needToCheckEmptinesss = other.needToCheckEmptinesss;
  206. table.malloc ((size_t) (jmax (1, bounds.getHeight()) * lineStrideElements));
  207. copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight());
  208. return *this;
  209. }
  210. EdgeTable::~EdgeTable()
  211. {
  212. }
  213. //==============================================================================
  214. void EdgeTable::copyEdgeTableData (int* dest, const int destLineStride, const int* src, const int srcLineStride, int numLines) noexcept
  215. {
  216. while (--numLines >= 0)
  217. {
  218. memcpy (dest, src, (size_t) (src[0] * 2 + 1) * sizeof (int));
  219. src += srcLineStride;
  220. dest += destLineStride;
  221. }
  222. }
  223. void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept
  224. {
  225. // Convert the table from relative windings to absolute levels..
  226. int* lineStart = table;
  227. for (int i = bounds.getHeight(); --i >= 0;)
  228. {
  229. int* line = lineStart;
  230. lineStart += lineStrideElements;
  231. int num = *line;
  232. if (num == 0)
  233. continue;
  234. int level = 0;
  235. if (useNonZeroWinding)
  236. {
  237. while (--num > 0)
  238. {
  239. line += 2;
  240. level += *line;
  241. int corrected = abs (level);
  242. if (corrected >> 8)
  243. corrected = 255;
  244. *line = corrected;
  245. }
  246. }
  247. else
  248. {
  249. while (--num > 0)
  250. {
  251. line += 2;
  252. level += *line;
  253. int corrected = abs (level);
  254. if (corrected >> 8)
  255. {
  256. corrected &= 511;
  257. if (corrected >> 8)
  258. corrected = 511 - corrected;
  259. }
  260. *line = corrected;
  261. }
  262. }
  263. line[2] = 0; // force the last level to 0, just in case something went wrong in creating the table
  264. }
  265. }
  266. void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine)
  267. {
  268. if (newNumEdgesPerLine != maxEdgesPerLine)
  269. {
  270. maxEdgesPerLine = newNumEdgesPerLine;
  271. jassert (bounds.getHeight() > 0);
  272. const int newLineStrideElements = maxEdgesPerLine * 2 + 1;
  273. HeapBlock <int> newTable ((size_t) (bounds.getHeight() * newLineStrideElements));
  274. copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.getHeight());
  275. table.swapWith (newTable);
  276. lineStrideElements = newLineStrideElements;
  277. }
  278. }
  279. void EdgeTable::optimiseTable()
  280. {
  281. int maxLineElements = 0;
  282. for (int i = bounds.getHeight(); --i >= 0;)
  283. maxLineElements = jmax (maxLineElements, table [i * lineStrideElements]);
  284. remapTableForNumEdges (maxLineElements);
  285. }
  286. void EdgeTable::addEdgePoint (const int x, const int y, const int winding)
  287. {
  288. jassert (y >= 0 && y < bounds.getHeight());
  289. int* line = table + lineStrideElements * y;
  290. const int numPoints = line[0];
  291. int n = numPoints << 1;
  292. if (n > 0)
  293. {
  294. while (n > 0)
  295. {
  296. const int cx = line [n - 1];
  297. if (cx <= x)
  298. {
  299. if (cx == x)
  300. {
  301. line [n] += winding;
  302. return;
  303. }
  304. break;
  305. }
  306. n -= 2;
  307. }
  308. if (numPoints >= maxEdgesPerLine)
  309. {
  310. remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine);
  311. jassert (numPoints < maxEdgesPerLine);
  312. line = table + lineStrideElements * y;
  313. }
  314. memmove (line + (n + 3), line + (n + 1), sizeof (int) * (size_t) ((numPoints << 1) - n));
  315. }
  316. line [n + 1] = x;
  317. line [n + 2] = winding;
  318. line[0]++;
  319. }
  320. void EdgeTable::translate (float dx, const int dy) noexcept
  321. {
  322. bounds.translate ((int) std::floor (dx), dy);
  323. int* lineStart = table;
  324. const int intDx = (int) (dx * 256.0f);
  325. for (int i = bounds.getHeight(); --i >= 0;)
  326. {
  327. int* line = lineStart;
  328. lineStart += lineStrideElements;
  329. int num = *line++;
  330. while (--num >= 0)
  331. {
  332. *line += intDx;
  333. line += 2;
  334. }
  335. }
  336. }
  337. void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine)
  338. {
  339. jassert (y >= 0 && y < bounds.getHeight());
  340. int* dest = table + lineStrideElements * y;
  341. if (dest[0] == 0)
  342. return;
  343. int otherNumPoints = *otherLine;
  344. if (otherNumPoints == 0)
  345. {
  346. *dest = 0;
  347. return;
  348. }
  349. const int right = bounds.getRight() << 8;
  350. // optimise for the common case where our line lies entirely within a
  351. // single pair of points, as happens when clipping to a simple rect.
  352. if (otherNumPoints == 2 && otherLine[2] >= 255)
  353. {
  354. clipEdgeTableLineToRange (dest, otherLine[1], jmin (right, otherLine[3]));
  355. return;
  356. }
  357. ++otherLine;
  358. const size_t lineSizeBytes = (size_t) (dest[0] * 2 + 1) * sizeof (int);
  359. int* temp = static_cast<int*> (alloca (lineSizeBytes));
  360. memcpy (temp, dest, lineSizeBytes);
  361. const int* src1 = temp;
  362. int srcNum1 = *src1++;
  363. int x1 = *src1++;
  364. const int* src2 = otherLine;
  365. int srcNum2 = otherNumPoints;
  366. int x2 = *src2++;
  367. int destIndex = 0, destTotal = 0;
  368. int level1 = 0, level2 = 0;
  369. int lastX = std::numeric_limits<int>::min(), lastLevel = 0;
  370. while (srcNum1 > 0 && srcNum2 > 0)
  371. {
  372. int nextX;
  373. if (x1 < x2)
  374. {
  375. nextX = x1;
  376. level1 = *src1++;
  377. x1 = *src1++;
  378. --srcNum1;
  379. }
  380. else if (x1 == x2)
  381. {
  382. nextX = x1;
  383. level1 = *src1++;
  384. level2 = *src2++;
  385. x1 = *src1++;
  386. x2 = *src2++;
  387. --srcNum1;
  388. --srcNum2;
  389. }
  390. else
  391. {
  392. nextX = x2;
  393. level2 = *src2++;
  394. x2 = *src2++;
  395. --srcNum2;
  396. }
  397. if (nextX > lastX)
  398. {
  399. if (nextX >= right)
  400. break;
  401. lastX = nextX;
  402. const int nextLevel = (level1 * (level2 + 1)) >> 8;
  403. jassert (isPositiveAndBelow (nextLevel, (int) 256));
  404. if (nextLevel != lastLevel)
  405. {
  406. if (destTotal >= maxEdgesPerLine)
  407. {
  408. dest[0] = destTotal;
  409. remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine);
  410. dest = table + lineStrideElements * y;
  411. }
  412. ++destTotal;
  413. lastLevel = nextLevel;
  414. dest[++destIndex] = nextX;
  415. dest[++destIndex] = nextLevel;
  416. }
  417. }
  418. }
  419. if (lastLevel > 0)
  420. {
  421. if (destTotal >= maxEdgesPerLine)
  422. {
  423. dest[0] = destTotal;
  424. remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine);
  425. dest = table + lineStrideElements * y;
  426. }
  427. ++destTotal;
  428. dest[++destIndex] = right;
  429. dest[++destIndex] = 0;
  430. }
  431. dest[0] = destTotal;
  432. #if JUCE_DEBUG
  433. int last = std::numeric_limits<int>::min();
  434. for (int i = 0; i < dest[0]; ++i)
  435. {
  436. jassert (dest[i * 2 + 1] > last);
  437. last = dest[i * 2 + 1];
  438. }
  439. jassert (dest [dest[0] * 2] == 0);
  440. #endif
  441. }
  442. void EdgeTable::clipEdgeTableLineToRange (int* dest, const int x1, const int x2) noexcept
  443. {
  444. int* lastItem = dest + (dest[0] * 2 - 1);
  445. if (x2 < lastItem[0])
  446. {
  447. if (x2 <= dest[1])
  448. {
  449. dest[0] = 0;
  450. return;
  451. }
  452. while (x2 < lastItem[-2])
  453. {
  454. --(dest[0]);
  455. lastItem -= 2;
  456. }
  457. lastItem[0] = x2;
  458. lastItem[1] = 0;
  459. }
  460. if (x1 > dest[1])
  461. {
  462. while (lastItem[0] > x1)
  463. lastItem -= 2;
  464. const int itemsRemoved = (int) (lastItem - (dest + 1)) / 2;
  465. if (itemsRemoved > 0)
  466. {
  467. dest[0] -= itemsRemoved;
  468. memmove (dest + 1, lastItem, (size_t) dest[0] * (sizeof (int) * 2));
  469. }
  470. dest[1] = x1;
  471. }
  472. }
  473. //==============================================================================
  474. void EdgeTable::clipToRectangle (const Rectangle<int>& r)
  475. {
  476. const Rectangle<int> clipped (r.getIntersection (bounds));
  477. if (clipped.isEmpty())
  478. {
  479. needToCheckEmptinesss = false;
  480. bounds.setHeight (0);
  481. }
  482. else
  483. {
  484. const int top = clipped.getY() - bounds.getY();
  485. const int bottom = clipped.getBottom() - bounds.getY();
  486. if (bottom < bounds.getHeight())
  487. bounds.setHeight (bottom);
  488. for (int i = top; --i >= 0;)
  489. table [lineStrideElements * i] = 0;
  490. if (clipped.getX() > bounds.getX() || clipped.getRight() < bounds.getRight())
  491. {
  492. const int x1 = clipped.getX() << 8;
  493. const int x2 = jmin (bounds.getRight(), clipped.getRight()) << 8;
  494. int* line = table + lineStrideElements * top;
  495. for (int i = bottom - top; --i >= 0;)
  496. {
  497. if (line[0] != 0)
  498. clipEdgeTableLineToRange (line, x1, x2);
  499. line += lineStrideElements;
  500. }
  501. }
  502. needToCheckEmptinesss = true;
  503. }
  504. }
  505. void EdgeTable::excludeRectangle (const Rectangle<int>& r)
  506. {
  507. const Rectangle<int> clipped (r.getIntersection (bounds));
  508. if (! clipped.isEmpty())
  509. {
  510. const int top = clipped.getY() - bounds.getY();
  511. const int bottom = clipped.getBottom() - bounds.getY();
  512. const int rectLine[] = { 4, std::numeric_limits<int>::min(), 255,
  513. clipped.getX() << 8, 0,
  514. clipped.getRight() << 8, 255,
  515. std::numeric_limits<int>::max(), 0 };
  516. for (int i = top; i < bottom; ++i)
  517. intersectWithEdgeTableLine (i, rectLine);
  518. needToCheckEmptinesss = true;
  519. }
  520. }
  521. void EdgeTable::clipToEdgeTable (const EdgeTable& other)
  522. {
  523. const Rectangle<int> clipped (other.bounds.getIntersection (bounds));
  524. if (clipped.isEmpty())
  525. {
  526. needToCheckEmptinesss = false;
  527. bounds.setHeight (0);
  528. }
  529. else
  530. {
  531. const int top = clipped.getY() - bounds.getY();
  532. const int bottom = clipped.getBottom() - bounds.getY();
  533. if (bottom < bounds.getHeight())
  534. bounds.setHeight (bottom);
  535. if (clipped.getRight() < bounds.getRight())
  536. bounds.setRight (clipped.getRight());
  537. int i = 0;
  538. for (i = top; --i >= 0;)
  539. table [lineStrideElements * i] = 0;
  540. const int* otherLine = other.table + other.lineStrideElements * (clipped.getY() - other.bounds.getY());
  541. for (i = top; i < bottom; ++i)
  542. {
  543. intersectWithEdgeTableLine (i, otherLine);
  544. otherLine += other.lineStrideElements;
  545. }
  546. needToCheckEmptinesss = true;
  547. }
  548. }
  549. void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels)
  550. {
  551. y -= bounds.getY();
  552. if (y < 0 || y >= bounds.getHeight())
  553. return;
  554. needToCheckEmptinesss = true;
  555. if (numPixels <= 0)
  556. {
  557. table [lineStrideElements * y] = 0;
  558. return;
  559. }
  560. int* tempLine = static_cast<int*> (alloca ((size_t) (numPixels * 2 + 4) * sizeof (int)));
  561. int destIndex = 0, lastLevel = 0;
  562. while (--numPixels >= 0)
  563. {
  564. const int alpha = *mask;
  565. mask += maskStride;
  566. if (alpha != lastLevel)
  567. {
  568. tempLine[++destIndex] = (x << 8);
  569. tempLine[++destIndex] = alpha;
  570. lastLevel = alpha;
  571. }
  572. ++x;
  573. }
  574. if (lastLevel > 0)
  575. {
  576. tempLine[++destIndex] = (x << 8);
  577. tempLine[++destIndex] = 0;
  578. }
  579. tempLine[0] = destIndex >> 1;
  580. intersectWithEdgeTableLine (y, tempLine);
  581. }
  582. bool EdgeTable::isEmpty() noexcept
  583. {
  584. if (needToCheckEmptinesss)
  585. {
  586. needToCheckEmptinesss = false;
  587. int* t = table;
  588. for (int i = bounds.getHeight(); --i >= 0;)
  589. {
  590. if (t[0] > 1)
  591. return false;
  592. t += lineStrideElements;
  593. }
  594. bounds.setHeight (0);
  595. }
  596. return bounds.getHeight() == 0;
  597. }