|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869 |
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 7 End-User License
- Agreement and JUCE Privacy Policy.
-
- End User License Agreement: www.juce.com/juce-7-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- struct GraphicsFontHelpers
- {
- static auto compareFont (const Font& a, const Font& b) { return Font::compare (a, b); }
- };
-
- static auto operator< (const Font& a, const Font& b)
- {
- return GraphicsFontHelpers::compareFont (a, b);
- }
-
- template <typename T>
- static auto operator< (const Rectangle<T>& a, const Rectangle<T>& b)
- {
- const auto tie = [] (auto& t) { return std::make_tuple (t.getX(), t.getY(), t.getWidth(), t.getHeight()); };
- return tie (a) < tie (b);
- }
-
- static auto operator< (const Justification& a, const Justification& b)
- {
- return a.getFlags() < b.getFlags();
- }
-
- //==============================================================================
- namespace
- {
- struct ConfiguredArrangement
- {
- void draw (const Graphics& g) const { arrangement.draw (g, transform); }
-
- GlyphArrangement arrangement;
- AffineTransform transform;
- };
-
- template <typename ArrangementArgs>
- class GlyphArrangementCache final : public DeletedAtShutdown
- {
- public:
- GlyphArrangementCache() = default;
-
- ~GlyphArrangementCache() override
- {
- clearSingletonInstance();
- }
-
- template <typename ConfigureArrangement>
- void draw (const Graphics& g, ArrangementArgs&& args, ConfigureArrangement&& configureArrangement)
- {
- const ScopedTryLock stl (lock);
-
- if (! stl.isLocked())
- {
- configureArrangement (args).draw (g);
- return;
- }
-
- const auto cached = [&]
- {
- const auto iter = cache.find (args);
-
- if (iter != cache.end())
- {
- if (iter->second.cachePosition != cacheOrder.begin())
- cacheOrder.splice (cacheOrder.begin(), cacheOrder, iter->second.cachePosition);
-
- return iter;
- }
-
- auto result = cache.emplace (std::move (args), CachedGlyphArrangement { configureArrangement (args), {} }).first;
- cacheOrder.push_front (result);
- return result;
- }();
-
- cached->second.cachePosition = cacheOrder.begin();
- cached->second.configured.draw (g);
-
- while (cache.size() > cacheSize)
- {
- cache.erase (cacheOrder.back());
- cacheOrder.pop_back();
- }
- }
-
- JUCE_DECLARE_SINGLETON (GlyphArrangementCache<ArrangementArgs>, false)
-
- private:
- struct CachedGlyphArrangement
- {
- using CachePtr = typename std::map<ArrangementArgs, CachedGlyphArrangement>::const_iterator;
- ConfiguredArrangement configured;
- typename std::list<CachePtr>::const_iterator cachePosition;
- };
-
- static constexpr size_t cacheSize = 128;
- std::map<ArrangementArgs, CachedGlyphArrangement> cache;
- std::list<typename CachedGlyphArrangement::CachePtr> cacheOrder;
- CriticalSection lock;
- };
-
- template <typename ArrangementArgs>
- juce::SingletonHolder<GlyphArrangementCache<ArrangementArgs>, juce::CriticalSection, false> GlyphArrangementCache<ArrangementArgs>::singletonHolder;
-
- //==============================================================================
- template <typename Type>
- Rectangle<Type> coordsToRectangle (Type x, Type y, Type w, Type h) noexcept
- {
- #if JUCE_DEBUG
- const int maxVal = 0x3fffffff;
-
- jassertquiet ((int) x >= -maxVal && (int) x <= maxVal
- && (int) y >= -maxVal && (int) y <= maxVal
- && (int) w >= 0 && (int) w <= maxVal
- && (int) h >= 0 && (int) h <= maxVal);
- #endif
-
- return { x, y, w, h };
- }
- }
-
- //==============================================================================
- Graphics::Graphics (const Image& imageToDrawOnto)
- : contextHolder (imageToDrawOnto.createLowLevelContext()),
- context (*contextHolder)
- {
- jassert (imageToDrawOnto.isValid()); // Can't draw into a null image!
- }
-
- Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept
- : context (internalContext)
- {
- }
-
- //==============================================================================
- void Graphics::resetToDefaultState()
- {
- saveStateIfPending();
- context.setFill (FillType());
- context.setFont (Font());
- context.setInterpolationQuality (Graphics::mediumResamplingQuality);
- }
-
- bool Graphics::isVectorDevice() const
- {
- return context.isVectorDevice();
- }
-
- bool Graphics::reduceClipRegion (Rectangle<int> area)
- {
- saveStateIfPending();
- return context.clipToRectangle (area);
- }
-
- bool Graphics::reduceClipRegion (int x, int y, int w, int h)
- {
- return reduceClipRegion (coordsToRectangle (x, y, w, h));
- }
-
- bool Graphics::reduceClipRegion (const RectangleList<int>& clipRegion)
- {
- saveStateIfPending();
- return context.clipToRectangleList (clipRegion);
- }
-
- bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform)
- {
- saveStateIfPending();
- context.clipToPath (path, transform);
- return ! context.isClipEmpty();
- }
-
- bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform)
- {
- saveStateIfPending();
- context.clipToImageAlpha (image, transform);
- return ! context.isClipEmpty();
- }
-
- void Graphics::excludeClipRegion (Rectangle<int> rectangleToExclude)
- {
- saveStateIfPending();
- context.excludeClipRectangle (rectangleToExclude);
- }
-
- bool Graphics::isClipEmpty() const
- {
- return context.isClipEmpty();
- }
-
- Rectangle<int> Graphics::getClipBounds() const
- {
- return context.getClipBounds();
- }
-
- void Graphics::saveState()
- {
- saveStateIfPending();
- saveStatePending = true;
- }
-
- void Graphics::restoreState()
- {
- if (saveStatePending)
- saveStatePending = false;
- else
- context.restoreState();
- }
-
- void Graphics::saveStateIfPending()
- {
- if (saveStatePending)
- {
- saveStatePending = false;
- context.saveState();
- }
- }
-
- void Graphics::setOrigin (Point<int> newOrigin)
- {
- saveStateIfPending();
- context.setOrigin (newOrigin);
- }
-
- void Graphics::setOrigin (int x, int y)
- {
- setOrigin ({ x, y });
- }
-
- void Graphics::addTransform (const AffineTransform& transform)
- {
- saveStateIfPending();
- context.addTransform (transform);
- }
-
- bool Graphics::clipRegionIntersects (Rectangle<int> area) const
- {
- return context.clipRegionIntersects (area);
- }
-
- void Graphics::beginTransparencyLayer (float layerOpacity)
- {
- saveStateIfPending();
- context.beginTransparencyLayer (layerOpacity);
- }
-
- void Graphics::endTransparencyLayer()
- {
- context.endTransparencyLayer();
- }
-
- //==============================================================================
- void Graphics::setColour (Colour newColour)
- {
- saveStateIfPending();
- context.setFill (newColour);
- }
-
- void Graphics::setOpacity (float newOpacity)
- {
- saveStateIfPending();
- context.setOpacity (newOpacity);
- }
-
- void Graphics::setGradientFill (const ColourGradient& gradient)
- {
- setFillType (gradient);
- }
-
- void Graphics::setGradientFill (ColourGradient&& gradient)
- {
- setFillType (std::move (gradient));
- }
-
- void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity)
- {
- saveStateIfPending();
- context.setFill (FillType (imageToUse, AffineTransform::translation ((float) anchorX, (float) anchorY)));
- context.setOpacity (opacity);
- }
-
- void Graphics::setFillType (const FillType& newFill)
- {
- saveStateIfPending();
- context.setFill (newFill);
- }
-
- //==============================================================================
- void Graphics::setFont (const Font& newFont)
- {
- saveStateIfPending();
- context.setFont (newFont);
- }
-
- void Graphics::setFont (const float newFontHeight)
- {
- setFont (context.getFont().withHeight (newFontHeight));
- }
-
- Font Graphics::getCurrentFont() const
- {
- return context.getFont();
- }
-
- //==============================================================================
- void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY,
- Justification justification) const
- {
- if (text.isEmpty())
- return;
-
- // Don't pass any vertical placement flags to this method - they'll be ignored.
- jassert (justification.getOnlyVerticalFlags() == 0);
-
- auto flags = justification.getOnlyHorizontalFlags();
-
- if (flags == Justification::right && startX < context.getClipBounds().getX())
- return;
-
- if (flags == Justification::left && startX > context.getClipBounds().getRight())
- return;
-
- struct ArrangementArgs
- {
- auto tie() const noexcept { return std::tie (font, text, startX, baselineY); }
- bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); }
-
- const Font font;
- const String text;
- const int startX, baselineY, flags;
- };
-
- auto configureArrangement = [] (const ArrangementArgs& args)
- {
- AffineTransform transform;
- GlyphArrangement arrangement;
- arrangement.addLineOfText (args.font, args.text, (float) args.startX, (float) args.baselineY);
-
- if (args.flags != Justification::left)
- {
- auto w = arrangement.getBoundingBox (0, -1, true).getWidth();
-
- if ((args.flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0)
- w /= 2.0f;
-
- transform = AffineTransform::translation (-w, 0);
- }
-
- return ConfiguredArrangement { std::move (arrangement), std::move (transform) };
- };
-
- GlyphArrangementCache<ArrangementArgs>::getInstance()->draw (*this,
- { context.getFont(), text, startX, baselineY, flags },
- std::move (configureArrangement));
- }
-
- void Graphics::drawMultiLineText (const String& text, const int startX,
- const int baselineY, const int maximumLineWidth,
- Justification justification, const float leading) const
- {
- if (text.isEmpty() || startX >= context.getClipBounds().getRight())
- return;
-
- struct ArrangementArgs
- {
- auto tie() const noexcept { return std::tie (font, text, startX, baselineY, maximumLineWidth, justification, leading); }
- bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); }
-
- const Font font;
- const String text;
- const int startX, baselineY, maximumLineWidth;
- const Justification justification;
- const float leading;
- };
-
- auto configureArrangement = [] (const ArrangementArgs& args)
- {
- GlyphArrangement arrangement;
- arrangement.addJustifiedText (args.font, args.text,
- (float) args.startX, (float) args.baselineY, (float) args.maximumLineWidth,
- args.justification, args.leading);
- return ConfiguredArrangement { std::move (arrangement), {} };
- };
-
- GlyphArrangementCache<ArrangementArgs>::getInstance()->draw (*this,
- { context.getFont(), text, startX, baselineY, maximumLineWidth, justification, leading },
- std::move (configureArrangement));
- }
-
- void Graphics::drawText (const String& text, Rectangle<float> area,
- Justification justificationType, bool useEllipsesIfTooBig) const
- {
- if (text.isEmpty() || ! context.clipRegionIntersects (area.getSmallestIntegerContainer()))
- return;
-
- struct ArrangementArgs
- {
- auto tie() const noexcept { return std::tie (font, text, area, justificationType, useEllipsesIfTooBig); }
- bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); }
-
- const Font font;
- const String text;
- const Rectangle<float> area;
- const Justification justificationType;
- const bool useEllipsesIfTooBig;
- };
-
- auto configureArrangement = [] (const ArrangementArgs& args)
- {
- GlyphArrangement arrangement;
- arrangement.addCurtailedLineOfText (args.font, args.text, 0.0f, 0.0f,
- args.area.getWidth(), args.useEllipsesIfTooBig);
-
- arrangement.justifyGlyphs (0, arrangement.getNumGlyphs(),
- args.area.getX(), args.area.getY(), args.area.getWidth(), args.area.getHeight(),
- args.justificationType);
- return ConfiguredArrangement { std::move (arrangement), {} };
- };
-
- GlyphArrangementCache<ArrangementArgs>::getInstance()->draw (*this,
- { context.getFont(), text, area, justificationType, useEllipsesIfTooBig },
- std::move (configureArrangement));
- }
-
- void Graphics::drawText (const String& text, Rectangle<int> area,
- Justification justificationType, bool useEllipsesIfTooBig) const
- {
- drawText (text, area.toFloat(), justificationType, useEllipsesIfTooBig);
- }
-
- void Graphics::drawText (const String& text, int x, int y, int width, int height,
- Justification justificationType, const bool useEllipsesIfTooBig) const
- {
- drawText (text, coordsToRectangle (x, y, width, height), justificationType, useEllipsesIfTooBig);
- }
-
- void Graphics::drawFittedText (const String& text, Rectangle<int> area,
- Justification justification,
- const int maximumNumberOfLines,
- const float minimumHorizontalScale) const
- {
- if (text.isEmpty() || area.isEmpty() || ! context.clipRegionIntersects (area))
- return;
-
- struct ArrangementArgs
- {
- auto tie() const noexcept { return std::tie (font, text, area, justification, maximumNumberOfLines, minimumHorizontalScale); }
- bool operator< (const ArrangementArgs& other) const noexcept { return tie() < other.tie(); }
-
- const Font font;
- const String text;
- const Rectangle<float> area;
- const Justification justification;
- const int maximumNumberOfLines;
- const float minimumHorizontalScale;
- };
-
- auto configureArrangement = [] (const ArrangementArgs& args)
- {
- GlyphArrangement arrangement;
- arrangement.addFittedText (args.font, args.text,
- args.area.getX(), args.area.getY(),
- args.area.getWidth(), args.area.getHeight(),
- args.justification,
- args.maximumNumberOfLines,
- args.minimumHorizontalScale);
- return ConfiguredArrangement { std::move (arrangement), {} };
- };
-
- GlyphArrangementCache<ArrangementArgs>::getInstance()->draw (*this,
- { context.getFont(), text, area.toFloat(), justification, maximumNumberOfLines, minimumHorizontalScale },
- std::move (configureArrangement));
- }
-
- void Graphics::drawFittedText (const String& text, int x, int y, int width, int height,
- Justification justification,
- const int maximumNumberOfLines,
- const float minimumHorizontalScale) const
- {
- drawFittedText (text, coordsToRectangle (x, y, width, height),
- justification, maximumNumberOfLines, minimumHorizontalScale);
- }
-
- //==============================================================================
- void Graphics::fillRect (Rectangle<int> r) const
- {
- context.fillRect (r, false);
- }
-
- void Graphics::fillRect (Rectangle<float> r) const
- {
- context.fillRect (r);
- }
-
- void Graphics::fillRect (int x, int y, int width, int height) const
- {
- context.fillRect (coordsToRectangle (x, y, width, height), false);
- }
-
- void Graphics::fillRect (float x, float y, float width, float height) const
- {
- fillRect (coordsToRectangle (x, y, width, height));
- }
-
- void Graphics::fillRectList (const RectangleList<float>& rectangles) const
- {
- context.fillRectList (rectangles);
- }
-
- void Graphics::fillRectList (const RectangleList<int>& rects) const
- {
- for (auto& r : rects)
- context.fillRect (r, false);
- }
-
- void Graphics::fillAll() const
- {
- fillRect (context.getClipBounds());
- }
-
- void Graphics::fillAll (Colour colourToUse) const
- {
- if (! colourToUse.isTransparent())
- {
- auto clip = context.getClipBounds();
-
- context.saveState();
- context.setFill (colourToUse);
- context.fillRect (clip, false);
- context.restoreState();
- }
- }
-
-
- //==============================================================================
- void Graphics::fillPath (const Path& path) const
- {
- if (! (context.isClipEmpty() || path.isEmpty()))
- context.fillPath (path, AffineTransform());
- }
-
- void Graphics::fillPath (const Path& path, const AffineTransform& transform) const
- {
- if (! (context.isClipEmpty() || path.isEmpty()))
- context.fillPath (path, transform);
- }
-
- void Graphics::strokePath (const Path& path,
- const PathStrokeType& strokeType,
- const AffineTransform& transform) const
- {
- Path stroke;
- strokeType.createStrokedPath (stroke, path, transform, context.getPhysicalPixelScaleFactor());
- fillPath (stroke);
- }
-
- //==============================================================================
- void Graphics::drawRect (float x, float y, float width, float height, float lineThickness) const
- {
- drawRect (coordsToRectangle (x, y, width, height), lineThickness);
- }
-
- void Graphics::drawRect (int x, int y, int width, int height, int lineThickness) const
- {
- drawRect (coordsToRectangle (x, y, width, height), lineThickness);
- }
-
- void Graphics::drawRect (Rectangle<int> r, int lineThickness) const
- {
- drawRect (r.toFloat(), (float) lineThickness);
- }
-
- void Graphics::drawRect (Rectangle<float> r, const float lineThickness) const
- {
- jassert (r.getWidth() >= 0.0f && r.getHeight() >= 0.0f);
-
- RectangleList<float> rects;
- rects.addWithoutMerging (r.removeFromTop (lineThickness));
- rects.addWithoutMerging (r.removeFromBottom (lineThickness));
- rects.addWithoutMerging (r.removeFromLeft (lineThickness));
- rects.addWithoutMerging (r.removeFromRight (lineThickness));
- context.fillRectList (rects);
- }
-
- //==============================================================================
- void Graphics::fillEllipse (Rectangle<float> area) const
- {
- Path p;
- p.addEllipse (area);
- fillPath (p);
- }
-
- void Graphics::fillEllipse (float x, float y, float w, float h) const
- {
- fillEllipse (coordsToRectangle (x, y, w, h));
- }
-
- void Graphics::drawEllipse (float x, float y, float width, float height, float lineThickness) const
- {
- drawEllipse (coordsToRectangle (x, y, width, height), lineThickness);
- }
-
- void Graphics::drawEllipse (Rectangle<float> area, float lineThickness) const
- {
- Path p;
-
- if (area.getWidth() == area.getHeight())
- {
- // For a circle, we can avoid having to generate a stroke
- p.addEllipse (area.expanded (lineThickness * 0.5f));
- p.addEllipse (area.reduced (lineThickness * 0.5f));
- p.setUsingNonZeroWinding (false);
- fillPath (p);
- }
- else
- {
- p.addEllipse (area);
- strokePath (p, PathStrokeType (lineThickness));
- }
- }
-
- void Graphics::fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const
- {
- fillRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize);
- }
-
- void Graphics::fillRoundedRectangle (Rectangle<float> r, const float cornerSize) const
- {
- Path p;
- p.addRoundedRectangle (r, cornerSize);
- fillPath (p);
- }
-
- void Graphics::drawRoundedRectangle (float x, float y, float width, float height,
- float cornerSize, float lineThickness) const
- {
- drawRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize, lineThickness);
- }
-
- void Graphics::drawRoundedRectangle (Rectangle<float> r, float cornerSize, float lineThickness) const
- {
- Path p;
- p.addRoundedRectangle (r, cornerSize);
- strokePath (p, PathStrokeType (lineThickness));
- }
-
- void Graphics::drawArrow (Line<float> line, float lineThickness, float arrowheadWidth, float arrowheadLength) const
- {
- Path p;
- p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength);
- fillPath (p);
- }
-
- void Graphics::fillCheckerBoard (Rectangle<float> area, float checkWidth, float checkHeight,
- Colour colour1, Colour colour2) const
- {
- jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less!
-
- if (checkWidth > 0 && checkHeight > 0)
- {
- context.saveState();
-
- if (colour1 == colour2)
- {
- context.setFill (colour1);
- context.fillRect (area);
- }
- else
- {
- auto clipped = context.getClipBounds().getIntersection (area.getSmallestIntegerContainer());
-
- if (! clipped.isEmpty())
- {
- const int checkNumX = (int) (((float) clipped.getX() - area.getX()) / checkWidth);
- const int checkNumY = (int) (((float) clipped.getY() - area.getY()) / checkHeight);
- const float startX = area.getX() + (float) checkNumX * checkWidth;
- const float startY = area.getY() + (float) checkNumY * checkHeight;
- const float right = (float) clipped.getRight();
- const float bottom = (float) clipped.getBottom();
-
- for (int i = 0; i < 2; ++i)
- {
- int cy = i;
- RectangleList<float> checks;
-
- for (float y = startY; y < bottom; y += checkHeight)
- for (float x = startX + (cy++ & 1) * checkWidth; x < right; x += checkWidth * 2.0f)
- checks.addWithoutMerging ({ x, y, checkWidth, checkHeight });
-
- checks.clipTo (area);
- context.setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2);
- context.fillRectList (checks);
- }
- }
- }
-
- context.restoreState();
- }
- }
-
- //==============================================================================
- void Graphics::drawVerticalLine (const int x, float top, float bottom) const
- {
- if (top < bottom)
- context.fillRect (Rectangle<float> ((float) x, top, 1.0f, bottom - top));
- }
-
- void Graphics::drawHorizontalLine (const int y, float left, float right) const
- {
- if (left < right)
- context.fillRect (Rectangle<float> (left, (float) y, right - left, 1.0f));
- }
-
- void Graphics::drawLine (Line<float> line) const
- {
- context.drawLine (line);
- }
-
- void Graphics::drawLine (float x1, float y1, float x2, float y2) const
- {
- context.drawLine (Line<float> (x1, y1, x2, y2));
- }
-
- void Graphics::drawLine (float x1, float y1, float x2, float y2, float lineThickness) const
- {
- drawLine (Line<float> (x1, y1, x2, y2), lineThickness);
- }
-
- void Graphics::drawLine (Line<float> line, const float lineThickness) const
- {
- Path p;
- p.addLineSegment (line, lineThickness);
- fillPath (p);
- }
-
- void Graphics::drawDashedLine (Line<float> line, const float* dashLengths,
- int numDashLengths, float lineThickness, int n) const
- {
- jassert (n >= 0 && n < numDashLengths); // your start index must be valid!
-
- const Point<double> delta ((line.getEnd() - line.getStart()).toDouble());
- const double totalLen = delta.getDistanceFromOrigin();
-
- if (totalLen >= 0.1)
- {
- const double onePixAlpha = 1.0 / totalLen;
-
- for (double alpha = 0.0; alpha < 1.0;)
- {
- jassert (dashLengths[n] > 0); // can't have zero-length dashes!
-
- const double lastAlpha = alpha;
- alpha += dashLengths [n] * onePixAlpha;
- n = (n + 1) % numDashLengths;
-
- if ((n & 1) != 0)
- {
- const Line<float> segment (line.getStart() + (delta * lastAlpha).toFloat(),
- line.getStart() + (delta * jmin (1.0, alpha)).toFloat());
-
- if (lineThickness != 1.0f)
- drawLine (segment, lineThickness);
- else
- context.drawLine (segment);
- }
- }
- }
- }
-
- //==============================================================================
- void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality)
- {
- saveStateIfPending();
- context.setInterpolationQuality (newQuality);
- }
-
- //==============================================================================
- void Graphics::drawImageAt (const Image& imageToDraw, int x, int y, bool fillAlphaChannel) const
- {
- drawImageTransformed (imageToDraw,
- AffineTransform::translation ((float) x, (float) y),
- fillAlphaChannel);
- }
-
- void Graphics::drawImage (const Image& imageToDraw, Rectangle<float> targetArea,
- RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush) const
- {
- if (imageToDraw.isValid())
- drawImageTransformed (imageToDraw,
- placementWithinTarget.getTransformToFit (imageToDraw.getBounds().toFloat(), targetArea),
- fillAlphaChannelWithCurrentBrush);
- }
-
- void Graphics::drawImageWithin (const Image& imageToDraw, int dx, int dy, int dw, int dh,
- RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush) const
- {
- drawImage (imageToDraw, coordsToRectangle (dx, dy, dw, dh).toFloat(),
- placementWithinTarget, fillAlphaChannelWithCurrentBrush);
- }
-
- void Graphics::drawImage (const Image& imageToDraw,
- int dx, int dy, int dw, int dh,
- int sx, int sy, int sw, int sh,
- const bool fillAlphaChannelWithCurrentBrush) const
- {
- if (imageToDraw.isValid() && context.clipRegionIntersects (coordsToRectangle (dx, dy, dw, dh)))
- drawImageTransformed (imageToDraw.getClippedImage (coordsToRectangle (sx, sy, sw, sh)),
- AffineTransform::scale ((float) dw / (float) sw, (float) dh / (float) sh)
- .translated ((float) dx, (float) dy),
- fillAlphaChannelWithCurrentBrush);
- }
-
- void Graphics::drawImageTransformed (const Image& imageToDraw,
- const AffineTransform& transform,
- const bool fillAlphaChannelWithCurrentBrush) const
- {
- if (imageToDraw.isValid() && ! context.isClipEmpty())
- {
- if (fillAlphaChannelWithCurrentBrush)
- {
- context.saveState();
- context.clipToImageAlpha (imageToDraw, transform);
- fillAll();
- context.restoreState();
- }
- else
- {
- context.drawImage (imageToDraw, transform);
- }
- }
- }
-
- //==============================================================================
- Graphics::ScopedSaveState::ScopedSaveState (Graphics& g) : context (g)
- {
- context.saveState();
- }
-
- Graphics::ScopedSaveState::~ScopedSaveState()
- {
- context.restoreState();
- }
-
- } // namespace juce
|