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.

757 lines
20KB

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