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.

740 lines
20KB

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