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.

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