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.

758 lines
20KB

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