text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

//==============================================================================
/**
    Describes the layout and colours that should be used to paint a colour gradient.

    @see Graphics::setGradientFill
*/
class JUCE_API  ColourGradient  final
{
public:
    //==============================================================================
    /** Creates a gradient object.

        (x1, y1) is the location to draw with colour1. Likewise (x2, y2) is where
        colour2 should be. In between them there's a gradient.

        If isRadial is true, the colours form a circular gradient with (x1, y1) at
        its centre.

        The alpha transparencies of the colours are used, so note that
        if you blend from transparent to a solid colour, the RGB of the transparent
        colour will become visible in parts of the gradient. e.g. blending
        from Colour::transparentBlack to Colours::white will produce a
        muddy grey colour midway, but Colour::transparentWhite to Colours::white
        will be white all the way across.

        @see ColourGradient
    */
    ColourGradient (Colour colour1, float x1, float y1,
                    Colour colour2, float x2, float y2,
                    bool isRadial);

    /** Creates a gradient object.

        point1 is the location to draw with colour1. Likewise point2 is where
        colour2 should be. In between them there's a gradient.

        If isRadial is true, the colours form a circular gradient with point1 at
        its centre.

        The alpha transparencies of the colours are used, so note that
        if you blend from transparent to a solid colour, the RGB of the transparent
        colour will become visible in parts of the gradient. e.g. blending
        from Colour::transparentBlack to Colours::white will produce a
        muddy grey colour midway, but Colour::transparentWhite to Colours::white
        will be white all the way across.

        @see ColourGradient
    */
    ColourGradient (Colour colour1, Point<float> point1,
                    Colour colour2, Point<float> point2,
                    bool isRadial);

    /** Creates an uninitialised gradient.

        If you use this constructor instead of the other one, be sure to set all the
        object's public member variables before using it!
    */
    ColourGradient() noexcept;

    /** Destructor */
    ~ColourGradient();

    //==============================================================================
    /** Removes any colours that have been added.

        This will also remove any start and end colours, so the gradient won't work. You'll
        need to add more colours with addColour().
    */
    void clearColours();

    /** Adds a colour at a point along the length of the gradient.

        This allows the gradient to go through a spectrum of colours, instead of just a
        start and end colour.

        @param proportionAlongGradient      a value between 0 and 1.0, which is the proportion
                                            of the distance along the line between the two points
                                            at which the colour should occur.
        @param colour                       the colour that should be used at this point
        @returns the index at which the new point was added
    */
    int addColour (double proportionAlongGradient,
                   Colour colour);

    /** Removes one of the colours from the gradient. */
    void removeColour (int index);

    /** Multiplies the alpha value of all the colours by the given scale factor */
    void multiplyOpacity (float multiplier) noexcept;

    //==============================================================================
    /** Returns the number of colour-stops that have been added. */
    int getNumColours() const noexcept;

    /** Returns the position along the length of the gradient of the colour with this index.

        The index is from 0 to getNumColours() - 1. The return value will be between 0.0 and 1.0
    */
    double getColourPosition (int index) const noexcept;

    /** Returns the colour that was added with a given index.
        The index is from 0 to getNumColours() - 1.
    */
    Colour getColour (int index) const noexcept;

    /** Changes the colour at a given index.
        The index is from 0 to getNumColours() - 1.
    */
    void setColour (int index, Colour newColour) noexcept;

    /** Returns the an interpolated colour at any position along the gradient.
        @param position     the position along the gradient, between 0 and 1
    */
    Colour getColourAtPosition (double position) const noexcept;

    //==============================================================================
    /** Creates a set of interpolated premultiplied ARGB values.
        This will resize the HeapBlock, fill it with the colours, and will return the number of
        colours that it added.
        When calling this, the ColourGradient must have at least 2 colour stops specified.
    */
    int createLookupTable (const AffineTransform& transform, HeapBlock<PixelARGB>& resultLookupTable) const;

    /** Creates a set of interpolated premultiplied ARGB values.
        This will fill an array of a user-specified size with the gradient, interpolating to fit.
        The numEntries argument specifies the size of the array, and this size must be greater than zero.
        When calling this, the ColourGradient must have at least 2 colour stops specified.
    */
    void createLookupTable (PixelARGB* resultLookupTable, int numEntries) const noexcept;

    /** Returns true if all colours are opaque. */
    bool isOpaque() const noexcept;

    /** Returns true if all colours are completely transparent. */
    bool isInvisible() const noexcept;

    //==============================================================================
    Point<float> point1, point2;

    /** If true, the gradient should be filled circularly, centred around
        point1, with point2 defining a point on the circumference.

        If false, the gradient is linear between the two points.
    */
    bool isRadial;

    bool operator== (const ColourGradient&) const noexcept;
    bool operator!= (const ColourGradient&) const noexcept;

private:
    //==============================================================================
    struct ColourPoint
    {
        ColourPoint() noexcept {}

        ColourPoint (const double pos, Colour col) noexcept
            : position (pos), colour (col)
        {}

        bool operator== (const ColourPoint&) const noexcept;
        bool operator!= (const ColourPoint&) const noexcept;

        double position;
        Colour colour;
    };

    Array<ColourPoint> colours;

    JUCE_LEAK_DETECTOR (ColourGradient)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

//==============================================================================
/**
    Contains a set of predefined named colours (mostly standard HTML colours)

    @see Colour, Colours::greyLevel
*/
class Colours
{
public:
    static JUCE_API const Colour

    //==============================================================================
    transparentBlack,   /**< ARGB = 0x00000000 */
    transparentWhite,   /**< ARGB = 0x00ffffff */

    //==============================================================================
    black,              /**< ARGB = 0xff000000 */
    white,              /**< ARGB = 0xffffffff */
    blue,               /**< ARGB = 0xff0000ff */
    grey,               /**< ARGB = 0xff808080 */
    green,              /**< ARGB = 0xff008000 */
    red,                /**< ARGB = 0xffff0000 */
    yellow,             /**< ARGB = 0xffffff00 */

    //==============================================================================
    aliceblue,         antiquewhite,       aqua,                   aquamarine,
    azure,             beige,              bisque,                 blanchedalmond,
    blueviolet,        brown,              burlywood,              cadetblue,
    chartreuse,        chocolate,          coral,                  cornflowerblue,
    cornsilk,          crimson,            cyan,                   darkblue,
    darkcyan,          darkgoldenrod,      darkgrey,               darkgreen,
    darkkhaki,         darkmagenta,        darkolivegreen,         darkorange,
    darkorchid,        darkred,            darksalmon,             darkseagreen,
    darkslateblue,     darkslategrey,      darkturquoise,          darkviolet,
    deeppink,          deepskyblue,        dimgrey,                dodgerblue,
    firebrick,         floralwhite,        forestgreen,            fuchsia,
    gainsboro,         ghostwhite,         gold,                   goldenrod,
    greenyellow,       honeydew,           hotpink,                indianred,
    indigo,            ivory,              khaki,                  lavender,
    lavenderblush,     lawngreen,          lemonchiffon,           lightblue,
    lightcoral,        lightcyan,          lightgoldenrodyellow,   lightgreen,
    lightgrey,         lightpink,          lightsalmon,            lightseagreen,
    lightskyblue,      lightslategrey,     lightsteelblue,         lightyellow,
    lime,              limegreen,          linen,                  magenta,
    maroon,            mediumaquamarine,   mediumblue,             mediumorchid,
    mediumpurple,      mediumseagreen,     mediumslateblue,        mediumspringgreen,
    mediumturquoise,   mediumvioletred,    midnightblue,           mintcream,
    mistyrose,         moccasin,           navajowhite,            navy,
    oldlace,           olive,              olivedrab,              orange,
    orangered,         orchid,             palegoldenrod,          palegreen,
    paleturquoise,     palevioletred,      papayawhip,             peachpuff,
    peru,              pink,               plum,                   powderblue,
    purple,            rebeccapurple,      rosybrown,              royalblue,
    saddlebrown,       salmon,             sandybrown,             seagreen,
    seashell,          sienna,             silver,                 skyblue,
    slateblue,         slategrey,          snow,                   springgreen,
    steelblue,         tan,                teal,                   thistle,
    tomato,            turquoise,          violet,                 wheat,
    whitesmoke,        yellowgreen;

    /** Attempts to look up a string in the list of known colour names, and return
        the appropriate colour.

        A non-case-sensitive search is made of the list of predefined colours, and
        if a match is found, that colour is returned. If no match is found, the
        colour passed in as the defaultColour parameter is returned.
    */
    static JUCE_API Colour findColourForName (const String& colourName,
                                              Colour defaultColour);

private:
    //==============================================================================
    // this isn't a class you should ever instantiate - it's just here for the
    // static values in it.
    Colours();

    JUCE_DECLARE_NON_COPYABLE (Colours)
};

} // namespace juce

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

namespace ColourHelpers
{
    static uint8 floatToUInt8 (const float n) noexcept
    {
        return n <= 0.0f ? 0 : (n >= 1.0f ? 255 : static_cast<uint8> (n * 255.996f));
    }

    //==============================================================================
    struct HSB
    {
        HSB (Colour col) noexcept
        {
            const int r = col.getRed();
            const int g = col.getGreen();
            const int b = col.getBlue();

            const int hi = jmax (r, g, b);
            const int lo = jmin (r, g, b);

            if (hi != 0)
            {
                saturation = (hi - lo) / (float) hi;

                if (saturation > 0)
                {
                    const float invDiff = 1.0f / (hi - lo);

                    const float red   = (hi - r) * invDiff;
                    const float green = (hi - g) * invDiff;
                    const float blue  = (hi - b) * invDiff;

                    if (r == hi)
                        hue = blue - green;
                    else if (g == hi)
                        hue = 2.0f + red - blue;
                    else
                        hue = 4.0f + green - red;

                    hue *= 1.0f / 6.0f;

                    if (hue < 0)
                        ++hue;
                }
                else
                {
                    hue = 0;
                }
            }
            else
            {
                saturation = hue = 0;
            }

            brightness = hi / 255.0f;
        }

        Colour toColour (Colour original) const noexcept
        {
            return Colour (hue, saturation, brightness, original.getAlpha());
        }

        static PixelARGB toRGB (float h, float s, float v, const uint8 alpha) noexcept
        {
            v = jlimit (0.0f, 255.0f, v * 255.0f);
            const uint8 intV = (uint8) roundToInt (v);

            if (s <= 0)
                return PixelARGB (alpha, intV, intV, intV);

            s = jmin (1.0f, s);
            h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors
            const float f = h - std::floor (h);
            const uint8 x = (uint8) roundToInt (v * (1.0f - s));

            if (h < 1.0f)   return PixelARGB (alpha, intV,    (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x);
            if (h < 2.0f)   return PixelARGB (alpha,          (uint8) roundToInt (v * (1.0f - s * f)), intV, x);
            if (h < 3.0f)   return PixelARGB (alpha, x, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))));
            if (h < 4.0f)   return PixelARGB (alpha, x,       (uint8) roundToInt (v * (1.0f - s * f)), intV);
            if (h < 5.0f)   return PixelARGB (alpha,          (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x, intV);
            return                 PixelARGB (alpha, intV, x, (uint8) roundToInt (v * (1.0f - s * f)));
        }

        float hue, saturation, brightness;
    };

    //==============================================================================
    struct YIQ
    {
        YIQ (Colour c) noexcept
        {
            const float r = c.getFloatRed();
            const float g = c.getFloatGreen();
            const float b = c.getFloatBlue();

            y = 0.2999f * r + 0.5870f * g + 0.1140f * b;
            i = 0.5957f * r - 0.2744f * g - 0.3212f * b;
            q = 0.2114f * r - 0.5225f * g - 0.3113f * b;
            alpha = c.getFloatAlpha();
        }

        Colour toColour() const noexcept
        {
            return Colour::fromFloatRGBA (y + 0.9563f * i + 0.6210f * q,
                                          y - 0.2721f * i - 0.6474f * q,
                                          y - 1.1070f * i + 1.7046f * q,
                                          alpha);
        }

        float y, i, q, alpha;
    };
}

//==============================================================================
Colour::Colour() noexcept
    : argb (0, 0, 0, 0)
{
}

Colour::Colour (const Colour& other) noexcept
    : argb (other.argb)
{
}

Colour& Colour::operator= (const Colour& other) noexcept
{
    argb = other.argb;
    return *this;
}

bool Colour::operator== (const Colour& other) const noexcept    { return argb.getNativeARGB() == other.argb.getNativeARGB(); }
bool Colour::operator!= (const Colour& other) const noexcept    { return argb.getNativeARGB() != other.argb.getNativeARGB(); }

//==============================================================================
Colour::Colour (const uint32 col) noexcept
    : argb ((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff)
{
}

Colour::Colour (const uint8 red, const uint8 green, const uint8 blue) noexcept
{
    argb.setARGB (0xff, red, green, blue);
}

Colour Colour::fromRGB (const uint8 red, const uint8 green, const uint8 blue) noexcept
{
    return Colour (red, green, blue);
}

Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
{
    argb.setARGB (alpha, red, green, blue);
}

Colour Colour::fromRGBA (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
{
    return Colour (red, green, blue, alpha);
}

Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const float alpha) noexcept
{
    argb.setARGB (ColourHelpers::floatToUInt8 (alpha), red, green, blue);
}

Colour Colour::fromFloatRGBA (const float red, const float green, const float blue, const float alpha) noexcept
{
    return Colour (ColourHelpers::floatToUInt8 (red),
                   ColourHelpers::floatToUInt8 (green),
                   ColourHelpers::floatToUInt8 (blue), alpha);
}

Colour::Colour (const float hue, const float saturation, const float brightness, const float alpha) noexcept
    : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha)))
{
}

Colour Colour::fromHSV (const float hue, const float saturation, const float brightness, const float alpha) noexcept
{
    return Colour (hue, saturation, brightness, alpha);
}

Colour::Colour (const float hue, const float saturation, const float brightness, const uint8 alpha) noexcept
    : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, alpha))
{
}

Colour::Colour (PixelARGB argb_) noexcept
    : argb (argb_)
{
}

Colour::Colour (PixelRGB rgb) noexcept
    : argb (Colour (rgb.getInARGBMaskOrder()).argb)
{
}

Colour::Colour (PixelAlpha alpha) noexcept
    : argb (Colour (alpha.getInARGBMaskOrder()).argb)
{
}

Colour::~Colour() noexcept
{
}

//==============================================================================
const PixelARGB Colour::getPixelARGB() const noexcept
{
    PixelARGB p (argb);
    p.premultiply();
    return p;
}

uint32 Colour::getARGB() const noexcept
{
    return argb.getInARGBMaskOrder();
}

//==============================================================================
bool Colour::isTransparent() const noexcept
{
    return getAlpha() == 0;
}

bool Colour::isOpaque() const noexcept
{
    return getAlpha() == 0xff;
}

Colour Colour::withAlpha (const uint8 newAlpha) const noexcept
{
    PixelARGB newCol (argb);
    newCol.setAlpha (newAlpha);
    return Colour (newCol);
}

Colour Colour::withAlpha (const float newAlpha) const noexcept
{
    jassert (newAlpha >= 0 && newAlpha <= 1.0f);

    PixelARGB newCol (argb);
    newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha));
    return Colour (newCol);
}

Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept
{
    jassert (alphaMultiplier >= 0);

    PixelARGB newCol (argb);
    newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha())));
    return Colour (newCol);
}

//==============================================================================
Colour Colour::overlaidWith (Colour src) const noexcept
{
    const int destAlpha = getAlpha();

    if (destAlpha <= 0)
        return src;

    const int invA = 0xff - (int) src.getAlpha();
    const int resA = 0xff - (((0xff - destAlpha) * invA) >> 8);

    if (resA <= 0)
        return *this;

    const int da = (invA * destAlpha) / resA;

    return Colour ((uint8) (src.getRed()   + ((((int) getRed()   - src.getRed())   * da) >> 8)),
                   (uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)),
                   (uint8) (src.getBlue()  + ((((int) getBlue()  - src.getBlue())  * da) >> 8)),
                   (uint8) resA);
}

Colour Colour::interpolatedWith (Colour other, float proportionOfOther) const noexcept
{
    if (proportionOfOther <= 0)
        return *this;

    if (proportionOfOther >= 1.0f)
        return other;

    PixelARGB c1 (getPixelARGB());
    const PixelARGB c2 (other.getPixelARGB());
    c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f));
    c1.unpremultiply();

    return Colour (c1);
}

//==============================================================================
float Colour::getFloatRed() const noexcept      { return getRed()   / 255.0f; }
float Colour::getFloatGreen() const noexcept    { return getGreen() / 255.0f; }
float Colour::getFloatBlue() const noexcept     { return getBlue()  / 255.0f; }
float Colour::getFloatAlpha() const noexcept    { return getAlpha() / 255.0f; }

//==============================================================================
void Colour::getHSB (float& h, float& s, float& v) const noexcept
{
    const ColourHelpers::HSB hsb (*this);
    h = hsb.hue;
    s = hsb.saturation;
    v = hsb.brightness;
}

float Colour::getHue() const noexcept           { return ColourHelpers::HSB (*this).hue; }
float Colour::getSaturation() const noexcept    { return ColourHelpers::HSB (*this).saturation; }
float Colour::getBrightness() const noexcept    { return ColourHelpers::HSB (*this).brightness; }

Colour Colour::withHue (float h) const noexcept          { ColourHelpers::HSB hsb (*this); hsb.hue = h;        return hsb.toColour (*this); }
Colour Colour::withSaturation (float s) const noexcept   { ColourHelpers::HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); }
Colour Colour::withBrightness (float v) const noexcept   { ColourHelpers::HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); }

float Colour::getPerceivedBrightness() const noexcept
{
    return std::sqrt (0.241f * square (getFloatRed())
                    + 0.691f * square (getFloatGreen())
                    + 0.068f * square (getFloatBlue()));
}

//==============================================================================
Colour Colour::withRotatedHue (const float amountToRotate) const noexcept
{
    ColourHelpers::HSB hsb (*this);
    hsb.hue += amountToRotate;
    return hsb.toColour (*this);
}

Colour Colour::withMultipliedSaturation (const float amount) const noexcept
{
    ColourHelpers::HSB hsb (*this);
    hsb.saturation = jmin (1.0f, hsb.saturation * amount);
    return hsb.toColour (*this);
}

Colour Colour::withMultipliedBrightness (const float amount) const noexcept
{
    ColourHelpers::HSB hsb (*this);
    hsb.brightness = jmin (1.0f, hsb.brightness * amount);
    return hsb.toColour (*this);
}

//==============================================================================
Colour Colour::brighter (float amount) const noexcept
{
    amount = 1.0f / (1.0f + amount);

    return Colour ((uint8) (255 - (amount * (255 - getRed()))),
                   (uint8) (255 - (amount * (255 - getGreen()))),
                   (uint8) (255 - (amount * (255 - getBlue()))),
                   getAlpha());
}

Colour Colour::darker (float amount) const noexcept
{
    amount = 1.0f / (1.0f + amount);

    return Colour ((uint8) (amount * getRed()),
                   (uint8) (amount * getGreen()),
                   (uint8) (amount * getBlue()),
                   getAlpha());
}

//==============================================================================
Colour Colour::greyLevel (const float brightness) noexcept
{
    const uint8 level = ColourHelpers::floatToUInt8 (brightness);
    return Colour (level, level, level);
}

//==============================================================================
Colour Colour::contrasting (const float amount) const noexcept
{
   return overlaidWith ((getPerceivedBrightness() >= 0.5f
                           ? Colours::black
                           : Colours::white).withAlpha (amount));
}

Colour Colour::contrasting (Colour target, float minContrast) const noexcept
{
    const ColourHelpers::YIQ bg (*this);
    ColourHelpers::YIQ fg (target);

    if (std::abs (bg.y - fg.y) >= minContrast)
        return target;

    const float y1 = jmax (0.0f, bg.y - minContrast);
    const float y2 = jmin (1.0f, bg.y + minContrast);
    fg.y = (std::abs (y1 - bg.y) > std::abs (y2 - bg.y)) ? y1 : y2;

    return fg.toColour();
}

Colour Colour::contrasting (Colour colour1,
                            Colour colour2) noexcept
{
    const float b1 = colour1.getPerceivedBrightness();
    const float b2 = colour2.getPerceivedBrightness();
    float best = 0.0f;
    float bestDist = 0.0f;

    for (float i = 0.0f; i < 1.0f; i += 0.02f)
    {
        const float d1 = std::abs (i - b1);
        const float d2 = std::abs (i - b2);
        const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2);

        if (dist > bestDist)
        {
            best = i;
            bestDist = dist;
        }
    }

    return colour1.overlaidWith (colour2.withMultipliedAlpha (0.5f))
                  .withBrightness (best);
}

//==============================================================================
String Colour::toString() const
{
    return String::toHexString ((int) argb.getInARGBMaskOrder());
}

Colour Colour::fromString (StringRef encodedColourString)
{
    return Colour ((uint32) CharacterFunctions::HexParser<int>::parse (encodedColourString.text));
}

String Colour::toDisplayString (const bool includeAlphaValue) const
{
    return String::toHexString ((int) (argb.getInARGBMaskOrder() & (includeAlphaValue ? 0xffffffff : 0xffffff)))
                  .paddedLeft ('0', includeAlphaValue ? 8 : 6)
                  .toUpperCase();
}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image)
    : RenderingHelpers::StackBasedLowLevelGraphicsContext<RenderingHelpers::SoftwareRendererSavedState>
        (new RenderingHelpers::SoftwareRendererSavedState (image, image.getBounds()))
{
}

LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image, Point<int> origin,
                                                                    const RectangleList<int>& initialClip)
    : RenderingHelpers::StackBasedLowLevelGraphicsContext<RenderingHelpers::SoftwareRendererSavedState>
        (new RenderingHelpers::SoftwareRendererSavedState (image, initialClip, origin))
{
}

LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() {}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

//==============================================================================
/**
    Interface class for graphics context objects, used internally by the Graphics class.

    Users are not supposed to create instances of this class directly - do your drawing
    via the Graphics object instead.

    It's a base class for different types of graphics context, that may perform software-based
    or OS-accelerated rendering.

    E.g. the LowLevelGraphicsSoftwareRenderer renders onto an image in memory, but other
    subclasses could render directly to a windows HDC, a Quartz context, or an OpenGL
    context.
*/
class JUCE_API  LowLevelGraphicsContext
{
protected:
    //==============================================================================
    LowLevelGraphicsContext();

public:
    virtual ~LowLevelGraphicsContext();

    /** Returns true if this device is vector-based, e.g. a printer. */
    virtual bool isVectorDevice() const = 0;

    //==============================================================================
    /** Moves the origin to a new position.

        The coordinates are relative to the current origin, and indicate the new position
        of (0, 0).
    */
    virtual void setOrigin (Point<int>) = 0;
    virtual void addTransform (const AffineTransform&) = 0;
    virtual float getPhysicalPixelScaleFactor() = 0;

    virtual bool clipToRectangle (const Rectangle<int>&) = 0;
    virtual bool clipToRectangleList (const RectangleList<int>&) = 0;
    virtual void excludeClipRectangle (const Rectangle<int>&) = 0;
    virtual void clipToPath (const Path&, const AffineTransform&) = 0;
    virtual void clipToImageAlpha (const Image&, const AffineTransform&) = 0;

    virtual bool clipRegionIntersects (const Rectangle<int>&) = 0;
    virtual Rectangle<int> getClipBounds() const = 0;
    virtual bool isClipEmpty() const = 0;

    virtual void saveState() = 0;
    virtual void restoreState() = 0;

    virtual void beginTransparencyLayer (float opacity) = 0;
    virtual void endTransparencyLayer() = 0;

    //==============================================================================
    virtual void setFill (const FillType&) = 0;
    virtual void setOpacity (float) = 0;
    virtual void setInterpolationQuality (Graphics::ResamplingQuality) = 0;

    //==============================================================================
    virtual void fillRect (const Rectangle<int>&, bool replaceExistingContents) = 0;
    virtual void fillRect (const Rectangle<float>&) = 0;
    virtual void fillRectList (const RectangleList<float>&) = 0;
    virtual void fillPath (const Path&, const AffineTransform&) = 0;
    virtual void drawImage (const Image&, const AffineTransform&) = 0;
    virtual void drawLine (const Line<float>&) = 0;

    virtual void setFont (const Font&) = 0;
    virtual const Font& getFont() = 0;
    virtual void drawGlyph (int glyphNumber, const AffineTransform&) = 0;
    virtual bool drawTextLayout (const AttributedString&, const Rectangle<float>&)  { return false; }
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

//==============================================================================
/**
    A lowest-common-denominator implementation of LowLevelGraphicsContext that does all
    its rendering in memory.

    User code is not supposed to create instances of this class directly - do all your
    rendering via the Graphics class instead.
*/
class JUCE_API  LowLevelGraphicsSoftwareRenderer    : public RenderingHelpers::StackBasedLowLevelGraphicsContext<RenderingHelpers::SoftwareRendererSavedState>
{
public:
    //==============================================================================
    /** Creates a context to render into an image. */
    LowLevelGraphicsSoftwareRenderer (const Image& imageToRenderOnto);

    /** Creates a context to render into a clipped subsection of an image. */
    LowLevelGraphicsSoftwareRenderer (const Image& imageToRenderOnto, Point<int> origin,
                                      const RectangleList<int>& initialClip);

    /** Destructor. */
    ~LowLevelGraphicsSoftwareRenderer();

private:
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsSoftwareRenderer)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

namespace
{
    template <typename Type>
    Rectangle<Type> coordsToRectangle (Type x, Type y, Type w, Type h) noexcept
    {
       #if JUCE_DEBUG
        const int maxVal = 0x3fffffff;

        jassert ((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 };
    }
}

//==============================================================================
LowLevelGraphicsContext::LowLevelGraphicsContext() {}
LowLevelGraphicsContext::~LowLevelGraphicsContext() {}

//==============================================================================
Graphics::Graphics (const Image& imageToDrawOnto)
    : context (*imageToDrawOnto.createLowLevelContext()),
      contextToDelete (&context)
{
    jassert (imageToDrawOnto.isValid()); // Can't draw into a null image!
}

Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept
    : context (internalContext)
{
}

Graphics::~Graphics()
{
}

//==============================================================================
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 (const float newOpacity)
{
    saveStateIfPending();
    context.setOpacity (newOpacity);
}

void Graphics::setGradientFill (const ColourGradient& gradient)
{
    setFillType (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.isNotEmpty())
    {
        // 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;

        GlyphArrangement arr;
        arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY);

        if (flags != Justification::left)
        {
            auto w = arr.getBoundingBox (0, -1, true).getWidth();

            if ((flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0)
                w /= 2.0f;

            arr.draw (*this, AffineTransform::translation (-w, 0));
        }
        else
        {
            arr.draw (*this);
        }
    }
}

void Graphics::drawMultiLineText (const String& text, const int startX,
                                  const int baselineY, const int maximumLineWidth) const
{
    if (text.isNotEmpty()
         && startX < context.getClipBounds().getRight())
    {
        GlyphArrangement arr;
        arr.addJustifiedText (context.getFont(), text,
                              (float) startX, (float) baselineY, (float) maximumLineWidth,
                              Justification::left);
        arr.draw (*this);
    }
}

void Graphics::drawText (const String& text, Rectangle<float> area,
                         Justification justificationType, bool useEllipsesIfTooBig) const
{
    if (text.isNotEmpty() && context.clipRegionIntersects (area.getSmallestIntegerContainer()))
    {
        GlyphArrangement arr;
        arr.addCurtailedLineOfText (context.getFont(), text, 0.0f, 0.0f,
                                    area.getWidth(), useEllipsesIfTooBig);

        arr.justifyGlyphs (0, arr.getNumGlyphs(),
                           area.getX(), area.getY(), area.getWidth(), area.getHeight(),
                           justificationType);
        arr.draw (*this);
    }
}

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.isNotEmpty() && (! area.isEmpty()) && context.clipRegionIntersects (area))
    {
        GlyphArrangement arr;
        arr.addFittedText (context.getFont(), text,
                           (float) area.getX(), (float) area.getY(),
                           (float) area.getWidth(), (float) area.getHeight(),
                           justification,
                           maximumNumberOfLines,
                           minimumHorizontalScale);

        arr.draw (*this);
    }
}

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<int> area,
                                 const int checkWidth, const int 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, false);
        }
        else
        {
            auto clipped = context.getClipBounds().getIntersection (area);

            if (! clipped.isEmpty())
            {
                context.clipToRectangle (clipped);

                const int checkNumX = (clipped.getX() - area.getX()) / checkWidth;
                const int checkNumY = (clipped.getY() - area.getY()) / checkHeight;
                const int startX = area.getX() + checkNumX * checkWidth;
                const int startY = area.getY() + checkNumY * checkHeight;
                const int right  = clipped.getRight();
                const int bottom = clipped.getBottom();

                for (int i = 0; i < 2; ++i)
                {
                    context.setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2);

                    int cy = i;
                    for (int y = startY; y < bottom; y += checkHeight)
                        for (int x = startX + (cy++ & 1) * checkWidth; x < right; x += checkWidth * 2)
                            context.fillRect (Rectangle<int> (x, y, checkWidth, checkHeight), false);
                }
            }
        }

        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 (dw / (float) sw, 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


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

//==============================================================================
/**
    A graphics context, used for drawing a component or image.

    When a Component needs painting, a Graphics context is passed to its
    Component::paint() method, and this you then call methods within this
    object to actually draw the component's content.

    A Graphics can also be created from an image, to allow drawing directly onto
    that image.

    @see Component::paint
*/
class JUCE_API  Graphics  final
{
public:
    //==============================================================================
    /** Creates a Graphics object to draw directly onto the given image.

        The graphics object that is created will be set up to draw onto the image,
        with the context's clipping area being the entire size of the image, and its
        origin being the image's origin. To draw into a subsection of an image, use the
        reduceClipRegion() and setOrigin() methods.

        Obviously you shouldn't delete the image before this context is deleted.
    */
    explicit Graphics (const Image& imageToDrawOnto);

    /** Destructor. */
    ~Graphics();

    //==============================================================================
    /** Changes the current drawing colour.

        This sets the colour that will now be used for drawing operations - it also
        sets the opacity to that of the colour passed-in.

        If a brush is being used when this method is called, the brush will be deselected,
        and any subsequent drawing will be done with a solid colour brush instead.

        @see setOpacity
    */
    void setColour (Colour newColour);

    /** Changes the opacity to use with the current colour.

        If a solid colour is being used for drawing, this changes its opacity
        to this new value (i.e. it doesn't multiply the colour's opacity by this amount).

        If a gradient is being used, this will have no effect on it.

        A value of 0.0 is completely transparent, 1.0 is completely opaque.
    */
    void setOpacity (float newOpacity);

    /** Sets the context to use a gradient for its fill pattern.
    */
    void setGradientFill (const ColourGradient& gradient);

    /** Sets the context to use a tiled image pattern for filling.
        Make sure that you don't delete this image while it's still being used by
        this context!
    */
    void setTiledImageFill (const Image& imageToUse,
                            int anchorX, int anchorY,
                            float opacity);

    /** Changes the current fill settings.
        @see setColour, setGradientFill, setTiledImageFill
    */
    void setFillType (const FillType& newFill);

    //==============================================================================
    /** Changes the font to use for subsequent text-drawing functions.
        @see drawSingleLineText, drawMultiLineText, drawText, drawFittedText
    */
    void setFont (const Font& newFont);

    /** Changes the size of the currently-selected font.
        This is a convenient shortcut that changes the context's current font to a
        different size. The typeface won't be changed.
        @see Font
    */
    void setFont (float newFontHeight);

    /** Returns the currently selected font. */
    Font getCurrentFont() const;

    /** Draws a one-line text string.

        This will use the current colour (or brush) to fill the text. The font is the last
        one specified by setFont().

        @param text          the string to draw
        @param startX        the position to draw the left-hand edge of the text
        @param baselineY     the position of the text's baseline
        @param justification the horizontal flags indicate which end of the text string is
                             anchored at the specified point.
        @see drawMultiLineText, drawText, drawFittedText, GlyphArrangement::addLineOfText
    */
    void drawSingleLineText (const String& text,
                             int startX, int baselineY,
                             Justification justification = Justification::left) const;

    /** Draws text across multiple lines.

        This will break the text onto a new line where there's a new-line or
        carriage-return character, or at a word-boundary when the text becomes wider
        than the size specified by the maximumLineWidth parameter.

        @see setFont, drawSingleLineText, drawFittedText, GlyphArrangement::addJustifiedText
    */
    void drawMultiLineText (const String& text,
                            int startX, int baselineY,
                            int maximumLineWidth) const;

    /** Draws a line of text within a specified rectangle.

        The text will be positioned within the rectangle based on the justification
        flags passed-in. If the string is too long to fit inside the rectangle, it will
        either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig
        flag is true).

        @see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText
    */
    void drawText (const String& text,
                   int x, int y, int width, int height,
                   Justification justificationType,
                   bool useEllipsesIfTooBig = true) const;

    /** Draws a line of text within a specified rectangle.

        The text will be positioned within the rectangle based on the justification
        flags passed-in. If the string is too long to fit inside the rectangle, it will
        either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig
        flag is true).

        @see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText
    */
    void drawText (const String& text,
                   Rectangle<int> area,
                   Justification justificationType,
                   bool useEllipsesIfTooBig = true) const;

    /** Draws a line of text within a specified rectangle.

        The text will be positioned within the rectangle based on the justification
        flags passed-in. If the string is too long to fit inside the rectangle, it will
        either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig
        flag is true).

        @see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText
    */
    void drawText (const String& text,
                   Rectangle<float> area,
                   Justification justificationType,
                   bool useEllipsesIfTooBig = true) const;

    /** Tries to draw a text string inside a given space.

        This does its best to make the given text readable within the specified rectangle,
        so it useful for labelling things.

        If the text is too big, it'll be squashed horizontally or broken over multiple lines
        if the maximumLinesToUse value allows this. If the text just won't fit into the space,
        it'll cram as much as possible in there, and put some ellipsis at the end to show that
        it's been truncated.

        A Justification parameter lets you specify how the text is laid out within the rectangle,
        both horizontally and vertically.

        The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally
        to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you
        can set this value to 1.0f. Pass 0 if you want it to use a default value.

        @see GlyphArrangement::addFittedText
    */
    void drawFittedText (const String& text,
                         int x, int y, int width, int height,
                         Justification justificationFlags,
                         int maximumNumberOfLines,
                         float minimumHorizontalScale = 0.0f) const;

    /** Tries to draw a text string inside a given space.

        This does its best to make the given text readable within the specified rectangle,
        so it useful for labelling things.

        If the text is too big, it'll be squashed horizontally or broken over multiple lines
        if the maximumLinesToUse value allows this. If the text just won't fit into the space,
        it'll cram as much as possible in there, and put some ellipsis at the end to show that
        it's been truncated.

        A Justification parameter lets you specify how the text is laid out within the rectangle,
        both horizontally and vertically.

        The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally
        to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you
        can set this value to 1.0f. Pass 0 if you want it to use a default value.

        @see GlyphArrangement::addFittedText
    */
    void drawFittedText (const String& text,
                         Rectangle<int> area,
                         Justification justificationFlags,
                         int maximumNumberOfLines,
                         float minimumHorizontalScale = 0.0f) const;

    //==============================================================================
    /** Fills the context's entire clip region with the current colour or brush.

        (See also the fillAll (Colour) method which is a quick way of filling
        it with a given colour).
    */
    void fillAll() const;

    /** Fills the context's entire clip region with a given colour.

        This leaves the context's current colour and brush unchanged, it just
        uses the specified colour temporarily.
    */
    void fillAll (Colour colourToUse) const;

    //==============================================================================
    /** Fills a rectangle with the current colour or brush.
        @see drawRect, fillRoundedRectangle
    */
    void fillRect (Rectangle<int> rectangle) const;

    /** Fills a rectangle with the current colour or brush.
        @see drawRect, fillRoundedRectangle
    */
    void fillRect (Rectangle<float> rectangle) const;

    /** Fills a rectangle with the current colour or brush.
        @see drawRect, fillRoundedRectangle
    */
    void fillRect (int x, int y, int width, int height) const;

    /** Fills a rectangle with the current colour or brush.
        @see drawRect, fillRoundedRectangle
    */
    void fillRect (float x, float y, float width, float height) const;

    /** Fills a set of rectangles using the current colour or brush.
        If you have a lot of rectangles to draw, it may be more efficient
        to create a RectangleList and use this method than to call fillRect()
        multiple times.
    */
    void fillRectList (const RectangleList<float>& rectangles) const;

    /** Fills a set of rectangles using the current colour or brush.
        If you have a lot of rectangles to draw, it may be more efficient
        to create a RectangleList and use this method than to call fillRect()
        multiple times.
    */
    void fillRectList (const RectangleList<int>& rectangles) const;

    /** Uses the current colour or brush to fill a rectangle with rounded corners.
        @see drawRoundedRectangle, Path::addRoundedRectangle
    */
    void fillRoundedRectangle (float x, float y, float width, float height,
                               float cornerSize) const;

    /** Uses the current colour or brush to fill a rectangle with rounded corners.
        @see drawRoundedRectangle, Path::addRoundedRectangle
    */
    void fillRoundedRectangle (Rectangle<float> rectangle,
                               float cornerSize) const;

    /** Fills a rectangle with a checkerboard pattern, alternating between two colours. */
    void fillCheckerBoard (Rectangle<int> area,
                           int checkWidth, int checkHeight,
                           Colour colour1, Colour colour2) const;

    /** Draws a rectangular outline, using the current colour or brush.
        The lines are drawn inside the given rectangle, and greater line thicknesses extend inwards.
        @see fillRect
    */
    void drawRect (int x, int y, int width, int height, int lineThickness = 1) const;

    /** Draws a rectangular outline, using the current colour or brush.
        The lines are drawn inside the given rectangle, and greater line thicknesses extend inwards.
        @see fillRect
    */
    void drawRect (float x, float y, float width, float height, float lineThickness = 1.0f) const;

    /** Draws a rectangular outline, using the current colour or brush.
        The lines are drawn inside the given rectangle, and greater line thicknesses extend inwards.
        @see fillRect
    */
    void drawRect (Rectangle<int> rectangle, int lineThickness = 1) const;

    /** Draws a rectangular outline, using the current colour or brush.
        The lines are drawn inside the given rectangle, and greater line thicknesses extend inwards.
        @see fillRect
    */
    void drawRect (Rectangle<float> rectangle, float lineThickness = 1.0f) const;

    /** Uses the current colour or brush to draw the outline of a rectangle with rounded corners.
        @see fillRoundedRectangle, Path::addRoundedRectangle
    */
    void drawRoundedRectangle (float x, float y, float width, float height,
                               float cornerSize, float lineThickness) const;

    /** Uses the current colour or brush to draw the outline of a rectangle with rounded corners.
        @see fillRoundedRectangle, Path::addRoundedRectangle
    */
    void drawRoundedRectangle (Rectangle<float> rectangle,
                               float cornerSize, float lineThickness) const;

    //==============================================================================
    /** Fills an ellipse with the current colour or brush.
        The ellipse is drawn to fit inside the given rectangle.
        @see drawEllipse, Path::addEllipse
    */
    void fillEllipse (float x, float y, float width, float height) const;

    /** Fills an ellipse with the current colour or brush.
        The ellipse is drawn to fit inside the given rectangle.
        @see drawEllipse, Path::addEllipse
    */
    void fillEllipse (Rectangle<float> area) const;

    /** Draws an elliptical stroke using the current colour or brush.
        @see fillEllipse, Path::addEllipse
    */
    void drawEllipse (float x, float y, float width, float height,
                      float lineThickness) const;

    /** Draws an elliptical stroke using the current colour or brush.
        @see fillEllipse, Path::addEllipse
    */
    void drawEllipse (Rectangle<float> area, float lineThickness) const;

    //==============================================================================
    /** Draws a line between two points.
        The line is 1 pixel wide and drawn with the current colour or brush.
        TIP: If you're trying to draw horizontal or vertical lines, don't use this -
        it's better to use fillRect() instead unless you really need an angled line.
    */
    void drawLine (float startX, float startY, float endX, float endY) const;

    /** Draws a line between two points with a given thickness.
        TIP: If you're trying to draw horizontal or vertical lines, don't use this -
        it's better to use fillRect() instead unless you really need an angled line.
        @see Path::addLineSegment
    */
    void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const;

    /** Draws a line between two points.
        The line is 1 pixel wide and drawn with the current colour or brush.
        TIP: If you're trying to draw horizontal or vertical lines, don't use this -
        it's better to use fillRect() instead unless you really need an angled line.
    */
    void drawLine (Line<float> line) const;

    /** Draws a line between two points with a given thickness.
        @see Path::addLineSegment
        TIP: If you're trying to draw horizontal or vertical lines, don't use this -
        it's better to use fillRect() instead unless you really need an angled line.
    */
    void drawLine (Line<float> line, float lineThickness) const;

    /** Draws a dashed line using a custom set of dash-lengths.

        @param line             the line to draw
        @param dashLengths      a series of lengths to specify the on/off lengths - e.g.
                                { 4, 5, 6, 7 } will draw a line of 4 pixels, skip 5 pixels,
                                draw 6 pixels, skip 7 pixels, and then repeat.
        @param numDashLengths   the number of elements in the array (this must be an even number).
        @param lineThickness    the thickness of the line to draw
        @param dashIndexToStartFrom     the index in the dash-length array to use for the first segment
        @see PathStrokeType::createDashedStroke
    */
    void drawDashedLine (Line<float> line,
                         const float* dashLengths, int numDashLengths,
                         float lineThickness = 1.0f,
                         int dashIndexToStartFrom = 0) const;

    /** Draws a vertical line of pixels at a given x position.

        The x position is an integer, but the top and bottom of the line can be sub-pixel
        positions, and these will be anti-aliased if necessary.

        The bottom parameter must be greater than or equal to the top parameter.
    */
    void drawVerticalLine (int x, float top, float bottom) const;

    /** Draws a horizontal line of pixels at a given y position.

        The y position is an integer, but the left and right ends of the line can be sub-pixel
        positions, and these will be anti-aliased if necessary.

        The right parameter must be greater than or equal to the left parameter.
    */
    void drawHorizontalLine (int y, float left, float right) const;

    //==============================================================================
    /** Fills a path using the currently selected colour or brush. */
    void fillPath (const Path& path) const;

    /** Fills a path using the currently selected colour or brush, and adds a transform. */
    void fillPath (const Path& path, const AffineTransform& transform) const;

    /** Draws a path's outline using the currently selected colour or brush. */
    void strokePath (const Path& path,
                     const PathStrokeType& strokeType,
                     const AffineTransform& transform = {}) const;

    /** Draws a line with an arrowhead at its end.

        @param line             the line to draw
        @param lineThickness    the thickness of the line
        @param arrowheadWidth   the width of the arrow head (perpendicular to the line)
        @param arrowheadLength  the length of the arrow head (along the length of the line)
    */
    void drawArrow (Line<float> line,
                    float lineThickness,
                    float arrowheadWidth,
                    float arrowheadLength) const;

    //==============================================================================
    /** Types of rendering quality that can be specified when drawing images.

        @see Graphics::setImageResamplingQuality
    */
    enum ResamplingQuality
    {
        lowResamplingQuality     = 0,    /**< Just uses a nearest-neighbour algorithm for resampling. */
        mediumResamplingQuality  = 1,    /**< Uses bilinear interpolation for upsampling and area-averaging for downsampling. */
        highResamplingQuality    = 2,    /**< Uses bicubic interpolation for upsampling and area-averaging for downsampling. */
    };

    /** Changes the quality that will be used when resampling images.
        By default a Graphics object will be set to mediumRenderingQuality.
        @see Graphics::drawImage, Graphics::drawImageTransformed, Graphics::drawImageWithin
    */
    void setImageResamplingQuality (const ResamplingQuality newQuality);

    /** Draws an image.

        This will draw the whole of an image, positioning its top-left corner at the
        given coordinates, and keeping its size the same. This is the simplest image
        drawing method - the others give more control over the scaling and clipping
        of the images.

        Images are composited using the context's current opacity, so if you
        don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f)
        (or setColour() with an opaque colour) before drawing images.
    */
    void drawImageAt (const Image& imageToDraw, int topLeftX, int topLeftY,
                      bool fillAlphaChannelWithCurrentBrush = false) const;

    /** Draws part of an image, rescaling it to fit in a given target region.

        The specified area of the source image is rescaled and drawn to fill the
        specifed destination rectangle.

        Images are composited using the context's current opacity, so if you
        don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f)
        (or setColour() with an opaque colour) before drawing images.

        @param imageToDraw      the image to overlay
        @param destX            the left of the destination rectangle
        @param destY            the top of the destination rectangle
        @param destWidth        the width of the destination rectangle
        @param destHeight       the height of the destination rectangle
        @param sourceX          the left of the rectangle to copy from the source image
        @param sourceY          the top of the rectangle to copy from the source image
        @param sourceWidth      the width of the rectangle to copy from the source image
        @param sourceHeight     the height of the rectangle to copy from the source image
        @param fillAlphaChannelWithCurrentBrush     if true, then instead of drawing the source image's pixels,
                                                    the source image's alpha channel is used as a mask with
                                                    which to fill the destination using the current colour
                                                    or brush. (If the source is has no alpha channel, then
                                                    it will just fill the target with a solid rectangle)
        @see setImageResamplingQuality, drawImageAt, drawImageWithin, fillAlphaMap
    */
    void drawImage (const Image& imageToDraw,
                    int destX, int destY, int destWidth, int destHeight,
                    int sourceX, int sourceY, int sourceWidth, int sourceHeight,
                    bool fillAlphaChannelWithCurrentBrush = false) const;

    /** Draws an image, having applied an affine transform to it.

        This lets you throw the image around in some wacky ways, rotate it, shear,
        scale it, etc.

        Images are composited using the context's current opacity, so if you
        don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f)
        (or setColour() with an opaque colour) before drawing images.

        If fillAlphaChannelWithCurrentBrush is set to true, then the image's RGB channels
        are ignored and it is filled with the current brush, masked by its alpha channel.

        If you want to render only a subsection of an image, use Image::getClippedImage() to
        create the section that you need.

        @see setImageResamplingQuality, drawImage
    */
    void drawImageTransformed (const Image& imageToDraw,
                               const AffineTransform& transform,
                               bool fillAlphaChannelWithCurrentBrush = false) const;

    /** Draws an image to fit within a designated rectangle.

        @param imageToDraw              the source image to draw
        @param targetArea               the target rectangle to fit it into
        @param placementWithinTarget    this specifies how the image should be positioned
                                        within the target rectangle - see the RectanglePlacement
                                        class for more details about this.
        @param fillAlphaChannelWithCurrentBrush     if true, then instead of drawing the image, just its
                                                    alpha channel will be used as a mask with which to
                                                    draw with the current brush or colour. This is
                                                    similar to fillAlphaMap(), and see also drawImage()
        @see drawImage, drawImageTransformed, drawImageAt, RectanglePlacement
    */
    void drawImage (const Image& imageToDraw, Rectangle<float> targetArea,
                    RectanglePlacement placementWithinTarget = RectanglePlacement::stretchToFit,
                    bool fillAlphaChannelWithCurrentBrush = false) const;

    /** Draws an image to fit within a designated rectangle.

        If the image is too big or too small for the space, it will be rescaled
        to fit as nicely as it can do without affecting its aspect ratio. It will
        then be placed within the target rectangle according to the justification flags
        specified.

        @param imageToDraw              the source image to draw
        @param destX                    top-left of the target rectangle to fit it into
        @param destY                    top-left of the target rectangle to fit it into
        @param destWidth                size of the target rectangle to fit the image into
        @param destHeight               size of the target rectangle to fit the image into
        @param placementWithinTarget    this specifies how the image should be positioned
                                        within the target rectangle - see the RectanglePlacement
                                        class for more details about this.
        @param fillAlphaChannelWithCurrentBrush     if true, then instead of drawing the image, just its
                                                    alpha channel will be used as a mask with which to
                                                    draw with the current brush or colour. This is
                                                    similar to fillAlphaMap(), and see also drawImage()
        @see setImageResamplingQuality, drawImage, drawImageTransformed, drawImageAt, RectanglePlacement
    */
    void drawImageWithin (const Image& imageToDraw,
                          int destX, int destY, int destWidth, int destHeight,
                          RectanglePlacement placementWithinTarget,
                          bool fillAlphaChannelWithCurrentBrush = false) const;

    //==============================================================================
    /** Returns the position of the bounding box for the current clipping region.
        @see getClipRegion, clipRegionIntersects
    */
    Rectangle<int> getClipBounds() const;

    /** Checks whether a rectangle overlaps the context's clipping region.

        If this returns false, no part of the given area can be drawn onto, so this
        method can be used to optimise a component's paint() method, by letting it
        avoid drawing complex objects that aren't within the region being repainted.
    */
    bool clipRegionIntersects (Rectangle<int> area) const;

    /** Intersects the current clipping region with another region.

        @returns true if the resulting clipping region is non-zero in size
        @see setOrigin, clipRegionIntersects
    */
    bool reduceClipRegion (int x, int y, int width, int height);

    /** Intersects the current clipping region with another region.

        @returns true if the resulting clipping region is non-zero in size
        @see setOrigin, clipRegionIntersects
    */
    bool reduceClipRegion (Rectangle<int> area);

    /** Intersects the current clipping region with a rectangle list region.

        @returns true if the resulting clipping region is non-zero in size
        @see setOrigin, clipRegionIntersects
    */
    bool reduceClipRegion (const RectangleList<int>& clipRegion);

    /** Intersects the current clipping region with a path.

        @returns true if the resulting clipping region is non-zero in size
        @see reduceClipRegion
    */
    bool reduceClipRegion (const Path& path, const AffineTransform& transform = AffineTransform());

    /** Intersects the current clipping region with an image's alpha-channel.

        The current clipping path is intersected with the area covered by this image's
        alpha-channel, after the image has been transformed by the specified matrix.

        @param image    the image whose alpha-channel should be used. If the image doesn't
                        have an alpha-channel, it is treated as entirely opaque.
        @param transform    a matrix to apply to the image
        @returns true if the resulting clipping region is non-zero in size
        @see reduceClipRegion
    */
    bool reduceClipRegion (const Image& image, const AffineTransform& transform);

    /** Excludes a rectangle to stop it being drawn into. */
    void excludeClipRegion (Rectangle<int> rectangleToExclude);

    /** Returns true if no drawing can be done because the clip region is zero. */
    bool isClipEmpty() const;

    //==============================================================================
    /** Saves the current graphics state on an internal stack.
        To restore the state, use restoreState().
        @see ScopedSaveState
    */
    void saveState();

    /** Restores a graphics state that was previously saved with saveState().
        @see ScopedSaveState
    */
    void restoreState();

    /** Uses RAII to save and restore the state of a graphics context.
        On construction, this calls Graphics::saveState(), and on destruction it calls
        Graphics::restoreState() on the Graphics object that you supply.
    */
    class ScopedSaveState
    {
    public:
        ScopedSaveState (Graphics&);
        ~ScopedSaveState();

    private:
        Graphics& context;
        JUCE_DECLARE_NON_COPYABLE (ScopedSaveState)
    };

    //==============================================================================
    /** Begins rendering to an off-screen bitmap which will later be flattened onto the current
        context with the given opacity.

        The context uses an internal stack of temporary image layers to do this. When you've
        finished drawing to the layer, call endTransparencyLayer() to complete the operation and
        composite the finished layer. Every call to beginTransparencyLayer() MUST be matched
        by a corresponding call to endTransparencyLayer()!

        This call also saves the current state, and endTransparencyLayer() restores it.
    */
    void beginTransparencyLayer (float layerOpacity);

    /** Completes a drawing operation to a temporary semi-transparent buffer.
        See beginTransparencyLayer() for more details.
    */
    void endTransparencyLayer();

    /** Moves the position of the context's origin.

        This changes the position that the context considers to be (0, 0) to
        the specified position.

        So if you call setOrigin with (100, 100), then the position that was previously
        referred to as (100, 100) will subsequently be considered to be (0, 0).

        @see reduceClipRegion, addTransform
    */
    void setOrigin (Point<int> newOrigin);

    /** Moves the position of the context's origin.

        This changes the position that the context considers to be (0, 0) to
        the specified position.

        So if you call setOrigin (100, 100), then the position that was previously
        referred to as (100, 100) will subsequently be considered to be (0, 0).

        @see reduceClipRegion, addTransform
    */
    void setOrigin (int newOriginX, int newOriginY);

    /** Adds a transformation which will be performed on all the graphics operations that
        the context subsequently performs.

        After calling this, all the coordinates that are passed into the context will be
        transformed by this matrix.

        @see setOrigin
    */
    void addTransform (const AffineTransform& transform);

    /** Resets the current colour, brush, and font to default settings. */
    void resetToDefaultState();

    /** Returns true if this context is drawing to a vector-based device, such as a printer. */
    bool isVectorDevice() const;

    //==============================================================================
    /** Create a graphics that draws with a given low-level renderer.
        This method is intended for use only by people who know what they're doing.
        Note that the LowLevelGraphicsContext will NOT be deleted by this object.
    */
    Graphics (LowLevelGraphicsContext&) noexcept;

    /** @internal */
    LowLevelGraphicsContext& getInternalContext() const noexcept    { return context; }

private:
    //==============================================================================
    LowLevelGraphicsContext& context;
    ScopedPointer<LowLevelGraphicsContext> contextToDelete;

    bool saveStatePending = false;
    void saveStateIfPending();

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Graphics)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-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
{

//==============================================================================
/**
    An implementation of LowLevelGraphicsContext that turns the drawing operations
    into a PostScript document.

*/
class JUCE_API  LowLevelGraphicsPostScriptRenderer    : public LowLevelGraphicsContext
{
public:
    //==============================================================================
    LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript,
                                        const String& documentTitle,
                                        int totalWidth,
                                        int totalHeight);

    ~LowLevelGraphicsPostScriptRenderer();

    //==============================================================================
    bool isVectorDevice() const override;
    void setOrigin (Point<int>) override;
    void addTransform (const AffineTransform&) override;
    float getPhysicalPixelScaleFactor() override;

    bool clipToRectangle (const Rectangle<int>&) override;
    bool clipToRectangleList (const RectangleList<int>&) override;
    void excludeClipRectangle (const Rectangle<int>&) override;
    void clipToPath (const Path&, const AffineTransform&) override;
    void clipToImageAlpha (const Image&, const AffineTransform&) override;

    void saveState() override;
    void restoreState() override;

    void beginTransparencyLayer (float) override;
    void endTransparencyLayer() override;

    bool clipRegionIntersects (const Rectangle<int>&) override;
    Rectangle<int> getClipBounds() const override;
    bool isClipEmpty() const override;

    //==============================================================================
    void setFill (const FillType&) override;
    void setOpacity (float) override;
    void setInterpolationQuality (Graphics::ResamplingQuality) override;

    //==============================================================================
    void fillRect (const Rectangle<int>&, bool replaceExistingContents) override;
    void fillRect (const Rectangle<float>&) override;
    void fillRectList (const RectangleList<float>&) override;
    void fillPath (const Path&, const AffineTransform&) override;
    void drawImage (const Image&, const AffineTransform&) override;
    void drawLine (const Line <float>&) override;

    //==============================================================================
    const Font& getFont() override;
    void setFont (const Font&) override;
    void drawGlyph (int glyphNumber, const AffineTransform&) override;

protected:
    //==============================================================================
    OutputStream& out;
    int totalWidth, totalHeight;
    bool needToClip;
    Colour lastColour;

    struct SavedState
    {
        SavedState();
        SavedState& operator= (const SavedState&) = delete;
        ~SavedState();

        RectangleList<int> clip;
        int xOffset, yOffset;
        FillType fillType;
        Font font;
    };

    OwnedArray<SavedState> stateStack;

    void writeClip();
    void writeColour (Colour colour);
    void writePath (const Path&) const;
    void writeXY (float x, float y) const;
    void writeTransform (const AffineTransform&) const;
    void writeImage (const Image&, int sx, int sy, int maxW, int maxH) const;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsPostScriptRenderer)
};

} // namespace juce