Add remaining files
[juce-lv2.git] / juce / source / src / gui / graphics / contexts / juce_Graphics.cpp
blob100ab3b28b1b7d32ee90b98b456011fa357b359e
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
28 BEGIN_JUCE_NAMESPACE
30 #include "juce_Graphics.h"
31 #include "../fonts/juce_GlyphArrangement.h"
32 #include "../geometry/juce_PathStrokeType.h"
33 #include "juce_LowLevelGraphicsContext.h"
36 //==============================================================================
37 namespace
39 template <typename Type>
40 bool areCoordsSensibleNumbers (Type x, Type y, Type w, Type h)
42 const int maxVal = 0x3fffffff;
44 return (int) x >= -maxVal && (int) x <= maxVal
45 && (int) y >= -maxVal && (int) y <= maxVal
46 && (int) w >= -maxVal && (int) w <= maxVal
47 && (int) h >= -maxVal && (int) h <= maxVal;
51 //==============================================================================
52 LowLevelGraphicsContext::LowLevelGraphicsContext()
56 LowLevelGraphicsContext::~LowLevelGraphicsContext()
60 //==============================================================================
61 Graphics::Graphics (const Image& imageToDrawOnto)
62 : context (imageToDrawOnto.createLowLevelContext()),
63 contextToDelete (context),
64 saveStatePending (false)
68 Graphics::Graphics (LowLevelGraphicsContext* const internalContext) noexcept
69 : context (internalContext),
70 saveStatePending (false)
74 Graphics::~Graphics()
78 //==============================================================================
79 void Graphics::resetToDefaultState()
81 saveStateIfPending();
82 context->setFill (FillType());
83 context->setFont (Font());
84 context->setInterpolationQuality (Graphics::mediumResamplingQuality);
87 bool Graphics::isVectorDevice() const
89 return context->isVectorDevice();
92 bool Graphics::reduceClipRegion (const Rectangle<int>& area)
94 saveStateIfPending();
95 return context->clipToRectangle (area);
98 bool Graphics::reduceClipRegion (const int x, const int y, const int w, const int h)
100 return reduceClipRegion (Rectangle<int> (x, y, w, h));
103 bool Graphics::reduceClipRegion (const RectangleList& clipRegion)
105 saveStateIfPending();
106 return context->clipToRectangleList (clipRegion);
109 bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform)
111 saveStateIfPending();
112 context->clipToPath (path, transform);
113 return ! context->isClipEmpty();
116 bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform)
118 saveStateIfPending();
119 context->clipToImageAlpha (image, transform);
120 return ! context->isClipEmpty();
123 void Graphics::excludeClipRegion (const Rectangle<int>& rectangleToExclude)
125 saveStateIfPending();
126 context->excludeClipRectangle (rectangleToExclude);
129 bool Graphics::isClipEmpty() const
131 return context->isClipEmpty();
134 const Rectangle<int> Graphics::getClipBounds() const
136 return context->getClipBounds();
139 void Graphics::saveState()
141 saveStateIfPending();
142 saveStatePending = true;
145 void Graphics::restoreState()
147 if (saveStatePending)
148 saveStatePending = false;
149 else
150 context->restoreState();
153 void Graphics::saveStateIfPending()
155 if (saveStatePending)
157 saveStatePending = false;
158 context->saveState();
162 void Graphics::setOrigin (const int newOriginX, const int newOriginY)
164 saveStateIfPending();
165 context->setOrigin (newOriginX, newOriginY);
168 void Graphics::addTransform (const AffineTransform& transform)
170 saveStateIfPending();
171 context->addTransform (transform);
174 bool Graphics::clipRegionIntersects (const Rectangle<int>& area) const
176 return context->clipRegionIntersects (area);
179 void Graphics::beginTransparencyLayer (float layerOpacity)
181 saveStateIfPending();
182 context->beginTransparencyLayer (layerOpacity);
185 void Graphics::endTransparencyLayer()
187 context->endTransparencyLayer();
190 //==============================================================================
191 void Graphics::setColour (const Colour& newColour)
193 saveStateIfPending();
194 context->setFill (newColour);
197 void Graphics::setOpacity (const float newOpacity)
199 saveStateIfPending();
200 context->setOpacity (newOpacity);
203 void Graphics::setGradientFill (const ColourGradient& gradient)
205 setFillType (gradient);
208 void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity)
210 saveStateIfPending();
211 context->setFill (FillType (imageToUse, AffineTransform::translation ((float) anchorX, (float) anchorY)));
212 context->setOpacity (opacity);
215 void Graphics::setFillType (const FillType& newFill)
217 saveStateIfPending();
218 context->setFill (newFill);
221 //==============================================================================
222 void Graphics::setFont (const Font& newFont)
224 saveStateIfPending();
225 context->setFont (newFont);
228 void Graphics::setFont (const float newFontHeight, const int newFontStyleFlags)
230 saveStateIfPending();
231 Font f (context->getFont());
232 f.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0);
233 context->setFont (f);
236 Font Graphics::getCurrentFont() const
238 return context->getFont();
241 //==============================================================================
242 void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY) const
244 if (text.isNotEmpty()
245 && startX < context->getClipBounds().getRight())
247 GlyphArrangement arr;
248 arr.addLineOfText (context->getFont(), text, (float) startX, (float) baselineY);
249 arr.draw (*this);
253 void Graphics::drawTextAsPath (const String& text, const AffineTransform& transform) const
255 if (text.isNotEmpty())
257 GlyphArrangement arr;
258 arr.addLineOfText (context->getFont(), text, 0.0f, 0.0f);
259 arr.draw (*this, transform);
263 void Graphics::drawMultiLineText (const String& text, const int startX, const int baselineY, const int maximumLineWidth) const
265 if (text.isNotEmpty()
266 && startX < context->getClipBounds().getRight())
268 GlyphArrangement arr;
269 arr.addJustifiedText (context->getFont(), text,
270 (float) startX, (float) baselineY, (float) maximumLineWidth,
271 Justification::left);
272 arr.draw (*this);
276 void Graphics::drawText (const String& text,
277 const int x, const int y, const int width, const int height,
278 const Justification& justificationType,
279 const bool useEllipsesIfTooBig) const
281 if (text.isNotEmpty() && context->clipRegionIntersects (Rectangle<int> (x, y, width, height)))
283 GlyphArrangement arr;
285 arr.addCurtailedLineOfText (context->getFont(), text,
286 0.0f, 0.0f, (float) width,
287 useEllipsesIfTooBig);
289 arr.justifyGlyphs (0, arr.getNumGlyphs(),
290 (float) x, (float) y, (float) width, (float) height,
291 justificationType);
292 arr.draw (*this);
296 void Graphics::drawFittedText (const String& text,
297 const int x, const int y, const int width, const int height,
298 const Justification& justification,
299 const int maximumNumberOfLines,
300 const float minimumHorizontalScale) const
302 if (text.isNotEmpty()
303 && width > 0 && height > 0
304 && context->clipRegionIntersects (Rectangle<int> (x, y, width, height)))
306 GlyphArrangement arr;
308 arr.addFittedText (context->getFont(), text,
309 (float) x, (float) y, (float) width, (float) height,
310 justification,
311 maximumNumberOfLines,
312 minimumHorizontalScale);
314 arr.draw (*this);
318 //==============================================================================
319 void Graphics::fillRect (int x, int y, int width, int height) const
321 // passing in a silly number can cause maths problems in rendering!
322 jassert (areCoordsSensibleNumbers (x, y, width, height));
324 context->fillRect (Rectangle<int> (x, y, width, height), false);
327 void Graphics::fillRect (const Rectangle<int>& r) const
329 context->fillRect (r, false);
332 void Graphics::fillRect (const float x, const float y, const float width, const float height) const
334 // passing in a silly number can cause maths problems in rendering!
335 jassert (areCoordsSensibleNumbers (x, y, width, height));
337 Path p;
338 p.addRectangle (x, y, width, height);
339 fillPath (p);
342 void Graphics::setPixel (int x, int y) const
344 context->fillRect (Rectangle<int> (x, y, 1, 1), false);
347 void Graphics::fillAll() const
349 fillRect (context->getClipBounds());
352 void Graphics::fillAll (const Colour& colourToUse) const
354 if (! colourToUse.isTransparent())
356 const Rectangle<int> clip (context->getClipBounds());
358 context->saveState();
359 context->setFill (colourToUse);
360 context->fillRect (clip, false);
361 context->restoreState();
366 //==============================================================================
367 void Graphics::fillPath (const Path& path, const AffineTransform& transform) const
369 if ((! context->isClipEmpty()) && ! path.isEmpty())
370 context->fillPath (path, transform);
373 void Graphics::strokePath (const Path& path,
374 const PathStrokeType& strokeType,
375 const AffineTransform& transform) const
377 Path stroke;
378 strokeType.createStrokedPath (stroke, path, transform, context->getScaleFactor());
379 fillPath (stroke);
382 //==============================================================================
383 void Graphics::drawRect (const int x, const int y, const int width, const int height,
384 const int lineThickness) const
386 // passing in a silly number can cause maths problems in rendering!
387 jassert (areCoordsSensibleNumbers (x, y, width, height));
389 context->fillRect (Rectangle<int> (x, y, width, lineThickness), false);
390 context->fillRect (Rectangle<int> (x, y + lineThickness, lineThickness, height - lineThickness * 2), false);
391 context->fillRect (Rectangle<int> (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2), false);
392 context->fillRect (Rectangle<int> (x, y + height - lineThickness, width, lineThickness), false);
395 void Graphics::drawRect (const float x, const float y, const float width, const float height, const float lineThickness) const
397 // passing in a silly number can cause maths problems in rendering!
398 jassert (areCoordsSensibleNumbers (x, y, width, height));
400 Path p;
401 p.addRectangle (x, y, width, lineThickness);
402 p.addRectangle (x, y + lineThickness, lineThickness, height - lineThickness * 2.0f);
403 p.addRectangle (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2.0f);
404 p.addRectangle (x, y + height - lineThickness, width, lineThickness);
405 fillPath (p);
408 void Graphics::drawRect (const Rectangle<int>& r, const int lineThickness) const
410 drawRect (r.getX(), r.getY(), r.getWidth(), r.getHeight(), lineThickness);
413 void Graphics::drawBevel (const int x, const int y, const int width, const int height,
414 const int bevelThickness, const Colour& topLeftColour, const Colour& bottomRightColour,
415 const bool useGradient, const bool sharpEdgeOnOutside) const
417 // passing in a silly number can cause maths problems in rendering!
418 jassert (areCoordsSensibleNumbers (x, y, width, height));
420 if (clipRegionIntersects (Rectangle<int> (x, y, width, height)))
422 context->saveState();
424 const float oldOpacity = 1.0f;//xxx state->colour.getFloatAlpha();
425 const float ramp = oldOpacity / bevelThickness;
427 for (int i = bevelThickness; --i >= 0;)
429 const float op = useGradient ? ramp * (sharpEdgeOnOutside ? bevelThickness - i : i)
430 : oldOpacity;
432 context->setFill (topLeftColour.withMultipliedAlpha (op));
433 context->fillRect (Rectangle<int> (x + i, y + i, width - i * 2, 1), false);
434 context->setFill (topLeftColour.withMultipliedAlpha (op * 0.75f));
435 context->fillRect (Rectangle<int> (x + i, y + i + 1, 1, height - i * 2 - 2), false);
436 context->setFill (bottomRightColour.withMultipliedAlpha (op));
437 context->fillRect (Rectangle<int> (x + i, y + height - i - 1, width - i * 2, 1), false);
438 context->setFill (bottomRightColour.withMultipliedAlpha (op * 0.75f));
439 context->fillRect (Rectangle<int> (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2), false);
442 context->restoreState();
446 //==============================================================================
447 void Graphics::fillEllipse (const float x, const float y, const float width, const float height) const
449 // passing in a silly number can cause maths problems in rendering!
450 jassert (areCoordsSensibleNumbers (x, y, width, height));
452 Path p;
453 p.addEllipse (x, y, width, height);
454 fillPath (p);
457 void Graphics::drawEllipse (const float x, const float y, const float width, const float height,
458 const float lineThickness) const
460 // passing in a silly number can cause maths problems in rendering!
461 jassert (areCoordsSensibleNumbers (x, y, width, height));
463 Path p;
464 p.addEllipse (x, y, width, height);
465 strokePath (p, PathStrokeType (lineThickness));
468 void Graphics::fillRoundedRectangle (const float x, const float y, const float width, const float height, const float cornerSize) const
470 // passing in a silly number can cause maths problems in rendering!
471 jassert (areCoordsSensibleNumbers (x, y, width, height));
473 Path p;
474 p.addRoundedRectangle (x, y, width, height, cornerSize);
475 fillPath (p);
478 void Graphics::fillRoundedRectangle (const Rectangle<float>& r, const float cornerSize) const
480 fillRoundedRectangle (r.getX(), r.getY(), r.getWidth(), r.getHeight(), cornerSize);
483 void Graphics::drawRoundedRectangle (const float x, const float y, const float width, const float height,
484 const float cornerSize, const float lineThickness) const
486 // passing in a silly number can cause maths problems in rendering!
487 jassert (areCoordsSensibleNumbers (x, y, width, height));
489 Path p;
490 p.addRoundedRectangle (x, y, width, height, cornerSize);
491 strokePath (p, PathStrokeType (lineThickness));
494 void Graphics::drawRoundedRectangle (const Rectangle<float>& r, const float cornerSize, const float lineThickness) const
496 drawRoundedRectangle (r.getX(), r.getY(), r.getWidth(), r.getHeight(), cornerSize, lineThickness);
499 void Graphics::drawArrow (const Line<float>& line, const float lineThickness, const float arrowheadWidth, const float arrowheadLength) const
501 Path p;
502 p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength);
503 fillPath (p);
506 void Graphics::fillCheckerBoard (const Rectangle<int>& area,
507 const int checkWidth, const int checkHeight,
508 const Colour& colour1, const Colour& colour2) const
510 jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less!
512 if (checkWidth > 0 && checkHeight > 0)
514 context->saveState();
516 if (colour1 == colour2)
518 context->setFill (colour1);
519 context->fillRect (area, false);
521 else
523 const Rectangle<int> clipped (context->getClipBounds().getIntersection (area));
525 if (! clipped.isEmpty())
527 context->clipToRectangle (clipped);
529 const int checkNumX = (clipped.getX() - area.getX()) / checkWidth;
530 const int checkNumY = (clipped.getY() - area.getY()) / checkHeight;
531 const int startX = area.getX() + checkNumX * checkWidth;
532 const int startY = area.getY() + checkNumY * checkHeight;
533 const int right = clipped.getRight();
534 const int bottom = clipped.getBottom();
536 for (int i = 0; i < 2; ++i)
538 context->setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2);
540 int cy = i;
541 for (int y = startY; y < bottom; y += checkHeight)
542 for (int x = startX + (cy++ & 1) * checkWidth; x < right; x += checkWidth * 2)
543 context->fillRect (Rectangle<int> (x, y, checkWidth, checkHeight), false);
548 context->restoreState();
552 //==============================================================================
553 void Graphics::drawVerticalLine (const int x, float top, float bottom) const
555 context->drawVerticalLine (x, top, bottom);
558 void Graphics::drawHorizontalLine (const int y, float left, float right) const
560 context->drawHorizontalLine (y, left, right);
563 void Graphics::drawLine (const float x1, const float y1, const float x2, const float y2) const
565 context->drawLine (Line<float> (x1, y1, x2, y2));
568 void Graphics::drawLine (const Line<float>& line) const
570 context->drawLine (line);
573 void Graphics::drawLine (const float x1, const float y1, const float x2, const float y2, const float lineThickness) const
575 drawLine (Line<float> (x1, y1, x2, y2), lineThickness);
578 void Graphics::drawLine (const Line<float>& line, const float lineThickness) const
580 Path p;
581 p.addLineSegment (line, lineThickness);
582 fillPath (p);
585 void Graphics::drawDashedLine (const Line<float>& line, const float* const dashLengths,
586 const int numDashLengths, const float lineThickness, int n) const
588 jassert (n >= 0 && n < numDashLengths); // your start index must be valid!
590 const Point<double> delta ((line.getEnd() - line.getStart()).toDouble());
591 const double totalLen = delta.getDistanceFromOrigin();
593 if (totalLen >= 0.1)
595 const double onePixAlpha = 1.0 / totalLen;
597 for (double alpha = 0.0; alpha < 1.0;)
599 jassert (dashLengths[n] > 0); // can't have zero-length dashes!
601 const double lastAlpha = alpha;
602 alpha = jmin (1.0, alpha + dashLengths [n] * onePixAlpha);
603 n = (n + 1) % numDashLengths;
605 if ((n & 1) != 0)
607 const Line<float> segment (line.getStart() + (delta * lastAlpha).toFloat(),
608 line.getStart() + (delta * alpha).toFloat());
610 if (lineThickness != 1.0f)
611 drawLine (segment, lineThickness);
612 else
613 context->drawLine (segment);
619 //==============================================================================
620 void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality)
622 saveStateIfPending();
623 context->setInterpolationQuality (newQuality);
626 //==============================================================================
627 void Graphics::drawImageAt (const Image& imageToDraw,
628 const int topLeftX, const int topLeftY,
629 const bool fillAlphaChannelWithCurrentBrush) const
631 const int imageW = imageToDraw.getWidth();
632 const int imageH = imageToDraw.getHeight();
634 drawImage (imageToDraw,
635 topLeftX, topLeftY, imageW, imageH,
636 0, 0, imageW, imageH,
637 fillAlphaChannelWithCurrentBrush);
640 void Graphics::drawImageWithin (const Image& imageToDraw,
641 const int destX, const int destY,
642 const int destW, const int destH,
643 const RectanglePlacement& placementWithinTarget,
644 const bool fillAlphaChannelWithCurrentBrush) const
646 // passing in a silly number can cause maths problems in rendering!
647 jassert (areCoordsSensibleNumbers (destX, destY, destW, destH));
649 if (imageToDraw.isValid())
651 const int imageW = imageToDraw.getWidth();
652 const int imageH = imageToDraw.getHeight();
654 if (imageW > 0 && imageH > 0)
656 double newX = 0.0, newY = 0.0;
657 double newW = imageW;
658 double newH = imageH;
660 placementWithinTarget.applyTo (newX, newY, newW, newH,
661 destX, destY, destW, destH);
663 if (newW > 0 && newH > 0)
665 drawImage (imageToDraw,
666 roundToInt (newX), roundToInt (newY),
667 roundToInt (newW), roundToInt (newH),
668 0, 0, imageW, imageH,
669 fillAlphaChannelWithCurrentBrush);
675 void Graphics::drawImage (const Image& imageToDraw,
676 int dx, int dy, int dw, int dh,
677 int sx, int sy, int sw, int sh,
678 const bool fillAlphaChannelWithCurrentBrush) const
680 // passing in a silly number can cause maths problems in rendering!
681 jassert (areCoordsSensibleNumbers (dx, dy, dw, dh));
682 jassert (areCoordsSensibleNumbers (sx, sy, sw, sh));
684 if (imageToDraw.isValid() && context->clipRegionIntersects (Rectangle<int> (dx, dy, dw, dh)))
686 drawImageTransformed (imageToDraw.getClippedImage (Rectangle<int> (sx, sy, sw, sh)),
687 AffineTransform::scale (dw / (float) sw, dh / (float) sh)
688 .translated ((float) dx, (float) dy),
689 fillAlphaChannelWithCurrentBrush);
693 void Graphics::drawImageTransformed (const Image& imageToDraw,
694 const AffineTransform& transform,
695 const bool fillAlphaChannelWithCurrentBrush) const
697 if (imageToDraw.isValid() && ! context->isClipEmpty())
699 if (fillAlphaChannelWithCurrentBrush)
701 context->saveState();
702 context->clipToImageAlpha (imageToDraw, transform);
703 fillAll();
704 context->restoreState();
706 else
708 context->drawImage (imageToDraw, transform, false);
713 //==============================================================================
714 Graphics::ScopedSaveState::ScopedSaveState (Graphics& g)
715 : context (g)
717 context.saveState();
720 Graphics::ScopedSaveState::~ScopedSaveState()
722 context.restoreState();
726 END_JUCE_NAMESPACE