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.

966 lines
33KB

  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_Graphics.h"
  21. #include "../fonts/juce_GlyphArrangement.h"
  22. #include "../geometry/juce_PathStrokeType.h"
  23. #include "juce_EdgeTable.h"
  24. #include "juce_LowLevelGraphicsContext.h"
  25. static const Graphics::ResamplingQuality defaultQuality = Graphics::mediumResamplingQuality;
  26. //==============================================================================
  27. #define MINIMUM_COORD -0x3fffffff
  28. #define MAXIMUM_COORD 0x3fffffff
  29. #undef ASSERT_COORDS_ARE_SENSIBLE_NUMBERS
  30. #define ASSERT_COORDS_ARE_SENSIBLE_NUMBERS(x, y, w, h) \
  31. jassert ((int) x >= MINIMUM_COORD \
  32. && (int) x <= MAXIMUM_COORD \
  33. && (int) y >= MINIMUM_COORD \
  34. && (int) y <= MAXIMUM_COORD \
  35. && (int) w >= MINIMUM_COORD \
  36. && (int) w <= MAXIMUM_COORD \
  37. && (int) h >= MINIMUM_COORD \
  38. && (int) h <= MAXIMUM_COORD);
  39. //==============================================================================
  40. LowLevelGraphicsContext::LowLevelGraphicsContext()
  41. {
  42. }
  43. LowLevelGraphicsContext::~LowLevelGraphicsContext()
  44. {
  45. }
  46. //==============================================================================
  47. Graphics::Graphics (Image& imageToDrawOnto) throw()
  48. : context (imageToDrawOnto.createLowLevelContext()),
  49. ownsContext (true),
  50. state (new GraphicsState()),
  51. saveStatePending (false)
  52. {
  53. }
  54. Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw()
  55. : context (internalContext),
  56. ownsContext (false),
  57. state (new GraphicsState()),
  58. saveStatePending (false)
  59. {
  60. }
  61. Graphics::~Graphics() throw()
  62. {
  63. delete state;
  64. if (ownsContext)
  65. delete context;
  66. }
  67. //==============================================================================
  68. void Graphics::resetToDefaultState() throw()
  69. {
  70. setColour (Colours::black);
  71. state->font.resetToDefaultState();
  72. state->quality = defaultQuality;
  73. }
  74. bool Graphics::isVectorDevice() const throw()
  75. {
  76. return context->isVectorDevice();
  77. }
  78. bool Graphics::reduceClipRegion (const int x, const int y,
  79. const int w, const int h) throw()
  80. {
  81. saveStateIfPending();
  82. return context->reduceClipRegion (x, y, w, h);
  83. }
  84. bool Graphics::reduceClipRegion (const RectangleList& clipRegion) throw()
  85. {
  86. saveStateIfPending();
  87. return context->reduceClipRegion (clipRegion);
  88. }
  89. void Graphics::excludeClipRegion (const int x, const int y,
  90. const int w, const int h) throw()
  91. {
  92. saveStateIfPending();
  93. context->excludeClipRegion (x, y, w, h);
  94. }
  95. bool Graphics::isClipEmpty() const throw()
  96. {
  97. return context->isClipEmpty();
  98. }
  99. const Rectangle Graphics::getClipBounds() const throw()
  100. {
  101. return context->getClipBounds();
  102. }
  103. void Graphics::saveState() throw()
  104. {
  105. saveStateIfPending();
  106. saveStatePending = true;
  107. }
  108. void Graphics::restoreState() throw()
  109. {
  110. if (saveStatePending)
  111. {
  112. saveStatePending = false;
  113. }
  114. else
  115. {
  116. const int stackSize = stateStack.size();
  117. if (stackSize > 0)
  118. {
  119. context->restoreState();
  120. delete state;
  121. state = stateStack.getUnchecked (stackSize - 1);
  122. stateStack.removeLast (1, false);
  123. }
  124. else
  125. {
  126. // Trying to call restoreState() more times than you've called saveState() !
  127. // Be careful to correctly match each saveState() with exactly one call to restoreState().
  128. jassertfalse
  129. }
  130. }
  131. }
  132. void Graphics::saveStateIfPending() throw()
  133. {
  134. if (saveStatePending)
  135. {
  136. saveStatePending = false;
  137. context->saveState();
  138. stateStack.add (new GraphicsState (*state));
  139. }
  140. }
  141. void Graphics::setOrigin (const int newOriginX,
  142. const int newOriginY) throw()
  143. {
  144. saveStateIfPending();
  145. context->setOrigin (newOriginX, newOriginY);
  146. }
  147. bool Graphics::clipRegionIntersects (const int x, const int y,
  148. const int w, const int h) const throw()
  149. {
  150. return context->clipRegionIntersects (x, y, w, h);
  151. }
  152. //==============================================================================
  153. void Graphics::setColour (const Colour& newColour) throw()
  154. {
  155. saveStateIfPending();
  156. state->colour = newColour;
  157. deleteAndZero (state->brush);
  158. }
  159. void Graphics::setOpacity (const float newOpacity) throw()
  160. {
  161. saveStateIfPending();
  162. state->colour = state->colour.withAlpha (newOpacity);
  163. }
  164. void Graphics::setBrush (const Brush* const newBrush) throw()
  165. {
  166. saveStateIfPending();
  167. delete state->brush;
  168. if (newBrush != 0)
  169. state->brush = newBrush->createCopy();
  170. else
  171. state->brush = 0;
  172. }
  173. //==============================================================================
  174. Graphics::GraphicsState::GraphicsState() throw()
  175. : colour (Colours::black),
  176. brush (0),
  177. quality (defaultQuality)
  178. {
  179. }
  180. Graphics::GraphicsState::GraphicsState (const GraphicsState& other) throw()
  181. : colour (other.colour),
  182. brush (other.brush != 0 ? other.brush->createCopy() : 0),
  183. font (other.font),
  184. quality (other.quality)
  185. {
  186. }
  187. Graphics::GraphicsState::~GraphicsState() throw()
  188. {
  189. delete brush;
  190. }
  191. //==============================================================================
  192. void Graphics::setFont (const Font& newFont) throw()
  193. {
  194. saveStateIfPending();
  195. state->font = newFont;
  196. }
  197. void Graphics::setFont (const float newFontHeight,
  198. const int newFontStyleFlags) throw()
  199. {
  200. saveStateIfPending();
  201. state->font.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0.0f);
  202. }
  203. //==============================================================================
  204. void Graphics::drawSingleLineText (const String& text,
  205. const int startX,
  206. const int baselineY) const throw()
  207. {
  208. if (text.isNotEmpty()
  209. && startX < context->getClipBounds().getRight())
  210. {
  211. GlyphArrangement arr;
  212. arr.addLineOfText (state->font, text, (float) startX, (float) baselineY);
  213. arr.draw (*this);
  214. }
  215. }
  216. void Graphics::drawTextAsPath (const String& text,
  217. const AffineTransform& transform) const throw()
  218. {
  219. if (text.isNotEmpty())
  220. {
  221. GlyphArrangement arr;
  222. arr.addLineOfText (state->font, text, 0.0f, 0.0f);
  223. arr.draw (*this, transform);
  224. }
  225. }
  226. void Graphics::drawMultiLineText (const String& text,
  227. const int startX,
  228. const int baselineY,
  229. const int maximumLineWidth) const throw()
  230. {
  231. if (text.isNotEmpty()
  232. && startX < context->getClipBounds().getRight())
  233. {
  234. GlyphArrangement arr;
  235. arr.addJustifiedText (state->font, text,
  236. (float) startX, (float) baselineY, (float) maximumLineWidth,
  237. Justification::left);
  238. arr.draw (*this);
  239. }
  240. }
  241. void Graphics::drawText (const String& text,
  242. const int x,
  243. const int y,
  244. const int width,
  245. const int height,
  246. const Justification& justificationType,
  247. const bool useEllipsesIfTooBig) const throw()
  248. {
  249. if (text.isNotEmpty() && context->clipRegionIntersects (x, y, width, height))
  250. {
  251. GlyphArrangement arr;
  252. arr.addCurtailedLineOfText (state->font, text,
  253. 0.0f, 0.0f, (float)width,
  254. useEllipsesIfTooBig);
  255. arr.justifyGlyphs (0, arr.getNumGlyphs(),
  256. (float) x, (float) y,
  257. (float) width, (float) height,
  258. justificationType);
  259. arr.draw (*this);
  260. }
  261. }
  262. void Graphics::drawFittedText (const String& text,
  263. const int x,
  264. const int y,
  265. const int width,
  266. const int height,
  267. const Justification& justification,
  268. const int maximumNumberOfLines,
  269. const float minimumHorizontalScale) const throw()
  270. {
  271. if (text.isNotEmpty()
  272. && width > 0 && height > 0
  273. && context->clipRegionIntersects (x, y, width, height))
  274. {
  275. GlyphArrangement arr;
  276. arr.addFittedText (state->font, text,
  277. (float) x, (float) y,
  278. (float) width, (float) height,
  279. justification,
  280. maximumNumberOfLines,
  281. minimumHorizontalScale);
  282. arr.draw (*this);
  283. }
  284. }
  285. //==============================================================================
  286. void Graphics::fillRect (int x,
  287. int y,
  288. int width,
  289. int height) const throw()
  290. {
  291. // passing in a silly number can cause maths problems in rendering!
  292. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  293. SolidColourBrush colourBrush (state->colour);
  294. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintRectangle (*context, x, y, width, height);
  295. }
  296. void Graphics::fillRect (const Rectangle& r) const throw()
  297. {
  298. fillRect (r.getX(),
  299. r.getY(),
  300. r.getWidth(),
  301. r.getHeight());
  302. }
  303. void Graphics::fillRect (const float x,
  304. const float y,
  305. const float width,
  306. const float height) const throw()
  307. {
  308. // passing in a silly number can cause maths problems in rendering!
  309. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  310. Path p;
  311. p.addRectangle (x, y, width, height);
  312. fillPath (p);
  313. }
  314. void Graphics::setPixel (int x, int y) const throw()
  315. {
  316. if (context->clipRegionIntersects (x, y, 1, 1))
  317. {
  318. SolidColourBrush colourBrush (state->colour);
  319. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintRectangle (*context, x, y, 1, 1);
  320. }
  321. }
  322. void Graphics::fillAll() const throw()
  323. {
  324. fillRect (context->getClipBounds());
  325. }
  326. void Graphics::fillAll (const Colour& colourToUse) const throw()
  327. {
  328. if (! colourToUse.isTransparent())
  329. {
  330. const Rectangle clip (context->getClipBounds());
  331. context->fillRectWithColour (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(),
  332. colourToUse, false);
  333. }
  334. }
  335. //==============================================================================
  336. void Graphics::fillPath (const Path& path,
  337. const AffineTransform& transform) const throw()
  338. {
  339. if ((! context->isClipEmpty()) && ! path.isEmpty())
  340. {
  341. SolidColourBrush colourBrush (state->colour);
  342. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintPath (*context, path, transform);
  343. }
  344. }
  345. void Graphics::strokePath (const Path& path,
  346. const PathStrokeType& strokeType,
  347. const AffineTransform& transform) const throw()
  348. {
  349. if ((! state->colour.isTransparent()) || state->brush != 0)
  350. {
  351. Path stroke;
  352. strokeType.createStrokedPath (stroke, path, transform);
  353. fillPath (stroke);
  354. }
  355. }
  356. //==============================================================================
  357. void Graphics::drawRect (const int x,
  358. const int y,
  359. const int width,
  360. const int height,
  361. const int lineThickness) const throw()
  362. {
  363. // passing in a silly number can cause maths problems in rendering!
  364. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  365. SolidColourBrush colourBrush (state->colour);
  366. Brush& b = (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush);
  367. b.paintRectangle (*context, x, y, width, lineThickness);
  368. b.paintRectangle (*context, x, y + lineThickness, lineThickness, height - lineThickness * 2);
  369. b.paintRectangle (*context, x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2);
  370. b.paintRectangle (*context, x, y + height - lineThickness, width, lineThickness);
  371. }
  372. void Graphics::drawRect (const float x,
  373. const float y,
  374. const float width,
  375. const float height,
  376. const float lineThickness) const throw()
  377. {
  378. // passing in a silly number can cause maths problems in rendering!
  379. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  380. Path p;
  381. p.addRectangle (x, y, width, lineThickness);
  382. p.addRectangle (x, y + lineThickness, lineThickness, height - lineThickness * 2.0f);
  383. p.addRectangle (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2.0f);
  384. p.addRectangle (x, y + height - lineThickness, width, lineThickness);
  385. fillPath (p);
  386. }
  387. void Graphics::drawRect (const Rectangle& r,
  388. const int lineThickness) const throw()
  389. {
  390. drawRect (r.getX(), r.getY(),
  391. r.getWidth(), r.getHeight(),
  392. lineThickness);
  393. }
  394. void Graphics::drawBevel (const int x,
  395. const int y,
  396. const int width,
  397. const int height,
  398. const int bevelThickness,
  399. const Colour& topLeftColour,
  400. const Colour& bottomRightColour,
  401. const bool useGradient,
  402. const bool sharpEdgeOnOutside) const throw()
  403. {
  404. // passing in a silly number can cause maths problems in rendering!
  405. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  406. if (clipRegionIntersects (x, y, width, height))
  407. {
  408. const float oldOpacity = state->colour.getFloatAlpha();
  409. const float ramp = oldOpacity / bevelThickness;
  410. for (int i = bevelThickness; --i >= 0;)
  411. {
  412. const float op = useGradient ? ramp * (sharpEdgeOnOutside ? bevelThickness - i : i)
  413. : oldOpacity;
  414. context->fillRectWithColour (x + i, y + i, width - i * 2, 1, topLeftColour.withMultipliedAlpha (op), false);
  415. context->fillRectWithColour (x + i, y + i + 1, 1, height - i * 2 - 2, topLeftColour.withMultipliedAlpha (op * 0.75f), false);
  416. context->fillRectWithColour (x + i, y + height - i - 1, width - i * 2, 1, bottomRightColour.withMultipliedAlpha (op), false);
  417. context->fillRectWithColour (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2, bottomRightColour.withMultipliedAlpha (op * 0.75f), false);
  418. }
  419. }
  420. }
  421. //==============================================================================
  422. void Graphics::fillEllipse (const float x,
  423. const float y,
  424. const float width,
  425. const float height) const throw()
  426. {
  427. // passing in a silly number can cause maths problems in rendering!
  428. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  429. Path p;
  430. p.addEllipse (x, y, width, height);
  431. fillPath (p);
  432. }
  433. void Graphics::drawEllipse (const float x,
  434. const float y,
  435. const float width,
  436. const float height,
  437. const float lineThickness) const throw()
  438. {
  439. // passing in a silly number can cause maths problems in rendering!
  440. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  441. Path p;
  442. p.addEllipse (x, y, width, height);
  443. strokePath (p, PathStrokeType (lineThickness));
  444. }
  445. void Graphics::fillRoundedRectangle (const float x,
  446. const float y,
  447. const float width,
  448. const float height,
  449. const float cornerSize) const throw()
  450. {
  451. // passing in a silly number can cause maths problems in rendering!
  452. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  453. Path p;
  454. p.addRoundedRectangle (x, y, width, height, cornerSize);
  455. fillPath (p);
  456. }
  457. void Graphics::fillRoundedRectangle (const Rectangle& r,
  458. const float cornerSize) const throw()
  459. {
  460. fillRoundedRectangle ((float) r.getX(),
  461. (float) r.getY(),
  462. (float) r.getWidth(),
  463. (float) r.getHeight(),
  464. cornerSize);
  465. }
  466. void Graphics::drawRoundedRectangle (const float x,
  467. const float y,
  468. const float width,
  469. const float height,
  470. const float cornerSize,
  471. const float lineThickness) const throw()
  472. {
  473. // passing in a silly number can cause maths problems in rendering!
  474. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height);
  475. Path p;
  476. p.addRoundedRectangle (x, y, width, height, cornerSize);
  477. strokePath (p, PathStrokeType (lineThickness));
  478. }
  479. void Graphics::drawRoundedRectangle (const Rectangle& r,
  480. const float cornerSize,
  481. const float lineThickness) const throw()
  482. {
  483. drawRoundedRectangle ((float) r.getX(),
  484. (float) r.getY(),
  485. (float) r.getWidth(),
  486. (float) r.getHeight(),
  487. cornerSize, lineThickness);
  488. }
  489. void Graphics::drawArrow (const float startX,
  490. const float startY,
  491. const float endX,
  492. const float endY,
  493. const float lineThickness,
  494. const float arrowheadWidth,
  495. const float arrowheadLength) const throw()
  496. {
  497. Path p;
  498. p.addArrow (startX, startY, endX, endY,
  499. lineThickness, arrowheadWidth, arrowheadLength);
  500. fillPath (p);
  501. }
  502. void Graphics::fillCheckerBoard (int x, int y,
  503. int width, int height,
  504. const int checkWidth,
  505. const int checkHeight,
  506. const Colour& colour1,
  507. const Colour& colour2) const throw()
  508. {
  509. jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less!
  510. if (checkWidth > 0 && checkHeight > 0)
  511. {
  512. if (colour1 == colour2)
  513. {
  514. context->fillRectWithColour (x, y, width, height, colour1, false);
  515. }
  516. else
  517. {
  518. const Rectangle clip (context->getClipBounds());
  519. const int right = jmin (x + width, clip.getRight());
  520. const int bottom = jmin (y + height, clip.getBottom());
  521. int cy = 0;
  522. while (y < bottom)
  523. {
  524. int cx = cy;
  525. for (int xx = x; xx < right; xx += checkWidth)
  526. context->fillRectWithColour (xx, y,
  527. jmin (checkWidth, right - xx),
  528. jmin (checkHeight, bottom - y),
  529. ((cx++ & 1) == 0) ? colour1 : colour2,
  530. false);
  531. ++cy;
  532. y += checkHeight;
  533. }
  534. }
  535. }
  536. }
  537. //==============================================================================
  538. void Graphics::drawVerticalLine (const int x, float top, float bottom) const throw()
  539. {
  540. SolidColourBrush colourBrush (state->colour);
  541. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintVerticalLine (*context, x, top, bottom);
  542. }
  543. void Graphics::drawHorizontalLine (const int y, float left, float right) const throw()
  544. {
  545. SolidColourBrush colourBrush (state->colour);
  546. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintHorizontalLine (*context, y, left, right);
  547. }
  548. void Graphics::drawLine (float x1, float y1,
  549. float x2, float y2) const throw()
  550. {
  551. if (! context->isClipEmpty())
  552. {
  553. SolidColourBrush colourBrush (state->colour);
  554. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintLine (*context, x1, y1, x2, y2);
  555. }
  556. }
  557. void Graphics::drawLine (const float startX,
  558. const float startY,
  559. const float endX,
  560. const float endY,
  561. const float lineThickness) const throw()
  562. {
  563. Path p;
  564. p.addLineSegment (startX, startY, endX, endY, lineThickness);
  565. fillPath (p);
  566. }
  567. void Graphics::drawLine (const Line& line) const throw()
  568. {
  569. drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY());
  570. }
  571. void Graphics::drawLine (const Line& line,
  572. const float lineThickness) const throw()
  573. {
  574. drawLine (line.getStartX(), line.getStartY(), line.getEndX(), line.getEndY(), lineThickness);
  575. }
  576. void Graphics::drawDashedLine (const float startX,
  577. const float startY,
  578. const float endX,
  579. const float endY,
  580. const float* const dashLengths,
  581. const int numDashLengths,
  582. const float lineThickness) const throw()
  583. {
  584. const double dx = endX - startX;
  585. const double dy = endY - startY;
  586. const double totalLen = juce_hypot (dx, dy);
  587. if (totalLen >= 0.5)
  588. {
  589. const double onePixAlpha = 1.0 / totalLen;
  590. double alpha = 0.0;
  591. float x = startX;
  592. float y = startY;
  593. int n = 0;
  594. while (alpha < 1.0f)
  595. {
  596. alpha = jmin (1.0, alpha + dashLengths[n++] * onePixAlpha);
  597. n = n % numDashLengths;
  598. const float oldX = x;
  599. const float oldY = y;
  600. x = (float) (startX + dx * alpha);
  601. y = (float) (startY + dy * alpha);
  602. if ((n & 1) != 0)
  603. {
  604. if (lineThickness != 1.0f)
  605. drawLine (oldX, oldY, x, y, lineThickness);
  606. else
  607. drawLine (oldX, oldY, x, y);
  608. }
  609. }
  610. }
  611. }
  612. //==============================================================================
  613. void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality) throw()
  614. {
  615. saveStateIfPending();
  616. state->quality = newQuality;
  617. }
  618. //==============================================================================
  619. void Graphics::drawImageAt (const Image* const imageToDraw,
  620. const int topLeftX,
  621. const int topLeftY,
  622. const bool fillAlphaChannelWithCurrentBrush) const throw()
  623. {
  624. if (imageToDraw != 0)
  625. {
  626. const int imageW = imageToDraw->getWidth();
  627. const int imageH = imageToDraw->getHeight();
  628. drawImage (imageToDraw,
  629. topLeftX, topLeftY, imageW, imageH,
  630. 0, 0, imageW, imageH,
  631. fillAlphaChannelWithCurrentBrush);
  632. }
  633. }
  634. void Graphics::drawImageWithin (const Image* const imageToDraw,
  635. const int destX,
  636. const int destY,
  637. const int destW,
  638. const int destH,
  639. const RectanglePlacement& placementWithinTarget,
  640. const bool fillAlphaChannelWithCurrentBrush) const throw()
  641. {
  642. // passing in a silly number can cause maths problems in rendering!
  643. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (destX, destY, destW, destH);
  644. if (imageToDraw != 0)
  645. {
  646. const int imageW = imageToDraw->getWidth();
  647. const int imageH = imageToDraw->getHeight();
  648. if (imageW > 0 && imageH > 0)
  649. {
  650. double newX = 0.0, newY = 0.0;
  651. double newW = imageW;
  652. double newH = imageH;
  653. placementWithinTarget.applyTo (newX, newY, newW, newH,
  654. destX, destY, destW, destH);
  655. if (newW > 0 && newH > 0)
  656. {
  657. drawImage (imageToDraw,
  658. roundDoubleToInt (newX), roundDoubleToInt (newY),
  659. roundDoubleToInt (newW), roundDoubleToInt (newH),
  660. 0, 0, imageW, imageH,
  661. fillAlphaChannelWithCurrentBrush);
  662. }
  663. }
  664. }
  665. }
  666. void Graphics::drawImage (const Image* const imageToDraw,
  667. int dx, int dy, int dw, int dh,
  668. int sx, int sy, int sw, int sh,
  669. const bool fillAlphaChannelWithCurrentBrush) const throw()
  670. {
  671. // passing in a silly number can cause maths problems in rendering!
  672. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (dx, dy, dw, dh);
  673. ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (sx, sy, sw, sh);
  674. if (imageToDraw == 0 || ! context->clipRegionIntersects (dx, dy, dw, dh))
  675. return;
  676. if (sw == dw && sh == dh)
  677. {
  678. if (sx < 0)
  679. {
  680. dx -= sx;
  681. dw += sx;
  682. sw += sx;
  683. sx = 0;
  684. }
  685. if (sx + sw > imageToDraw->getWidth())
  686. {
  687. const int amount = sx + sw - imageToDraw->getWidth();
  688. dw -= amount;
  689. sw -= amount;
  690. }
  691. if (sy < 0)
  692. {
  693. dy -= sy;
  694. dh += sy;
  695. sh += sy;
  696. sy = 0;
  697. }
  698. if (sy + sh > imageToDraw->getHeight())
  699. {
  700. const int amount = sy + sh - imageToDraw->getHeight();
  701. dh -= amount;
  702. sh -= amount;
  703. }
  704. if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0)
  705. return;
  706. if (fillAlphaChannelWithCurrentBrush)
  707. {
  708. SolidColourBrush colourBrush (state->colour);
  709. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush)
  710. .paintAlphaChannel (*context, *imageToDraw,
  711. dx - sx, dy - sy,
  712. dx, dy,
  713. dw, dh);
  714. }
  715. else
  716. {
  717. context->blendImage (*imageToDraw,
  718. dx, dy, dw, dh, sx, sy,
  719. state->colour.getFloatAlpha());
  720. }
  721. }
  722. else
  723. {
  724. if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0)
  725. return;
  726. if (fillAlphaChannelWithCurrentBrush)
  727. {
  728. if (imageToDraw->isRGB())
  729. {
  730. fillRect (dx, dy, dw, dh);
  731. }
  732. else
  733. {
  734. int tx = dx;
  735. int ty = dy;
  736. int tw = dw;
  737. int th = dh;
  738. if (context->getClipBounds().intersectRectangle (tx, ty, tw, th))
  739. {
  740. Image temp (imageToDraw->getFormat(), tw, th, true);
  741. Graphics g (temp);
  742. g.setImageResamplingQuality (state->quality);
  743. g.setOrigin (dx - tx, dy - ty);
  744. g.drawImage (imageToDraw,
  745. 0, 0, dw, dh,
  746. sx, sy, sw, sh,
  747. false);
  748. SolidColourBrush colourBrush (state->colour);
  749. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush)
  750. .paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th);
  751. }
  752. }
  753. }
  754. else
  755. {
  756. context->blendImageWarping (*imageToDraw,
  757. sx, sy, sw, sh,
  758. AffineTransform::translation ((float) -sx,
  759. (float) -sy)
  760. .scaled (dw / (float) sw,
  761. dh / (float) sh)
  762. .translated ((float) dx,
  763. (float) dy),
  764. state->colour.getFloatAlpha(),
  765. state->quality);
  766. }
  767. }
  768. }
  769. void Graphics::drawImageTransformed (const Image* const imageToDraw,
  770. int sourceClipX,
  771. int sourceClipY,
  772. int sourceClipWidth,
  773. int sourceClipHeight,
  774. const AffineTransform& transform,
  775. const bool fillAlphaChannelWithCurrentBrush) const throw()
  776. {
  777. if (imageToDraw != 0
  778. && (! context->isClipEmpty())
  779. && ! transform.isSingularity())
  780. {
  781. if (transform.isIdentity())
  782. {
  783. drawImage (imageToDraw,
  784. sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight,
  785. sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight,
  786. fillAlphaChannelWithCurrentBrush);
  787. }
  788. else if (fillAlphaChannelWithCurrentBrush)
  789. {
  790. Path p;
  791. p.addRectangle ((float) sourceClipX, (float) sourceClipY,
  792. (float) sourceClipWidth, (float) sourceClipHeight);
  793. p.applyTransform (transform);
  794. float dx, dy, dw, dh;
  795. p.getBounds (dx, dy, dw, dh);
  796. int tx = (int) dx;
  797. int ty = (int) dy;
  798. int tw = roundFloatToInt (dw) + 2;
  799. int th = roundFloatToInt (dh) + 2;
  800. if (context->getClipBounds().intersectRectangle (tx, ty, tw, th))
  801. {
  802. Image temp (imageToDraw->getFormat(), tw, th, true);
  803. Graphics g (temp);
  804. g.setImageResamplingQuality (state->quality);
  805. g.drawImageTransformed (imageToDraw,
  806. sourceClipX,
  807. sourceClipY,
  808. sourceClipWidth,
  809. sourceClipHeight,
  810. transform.translated ((float) -tx, (float) -ty),
  811. false);
  812. SolidColourBrush colourBrush (state->colour);
  813. (state->brush != 0 ? *(state->brush) : (Brush&) colourBrush).paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th);
  814. }
  815. }
  816. else
  817. {
  818. context->blendImageWarping (*imageToDraw,
  819. sourceClipX,
  820. sourceClipY,
  821. sourceClipWidth,
  822. sourceClipHeight,
  823. transform,
  824. state->colour.getFloatAlpha(),
  825. state->quality);
  826. }
  827. }
  828. }
  829. END_JUCE_NAMESPACE