diff --git a/build/win32/vc8/JUCE.vcproj b/build/win32/vc8/JUCE.vcproj index c210c1896a..127c440c33 100644 --- a/build/win32/vc8/JUCE.vcproj +++ b/build/win32/vc8/JUCE.vcproj @@ -2168,42 +2168,6 @@ - - - - - - - - - - - - - - - - - - diff --git a/extras/juce demo/src/demos/PathsAndTransformsDemo.cpp b/extras/juce demo/src/demos/PathsAndTransformsDemo.cpp index 135ed6dcff..af68d37e27 100644 --- a/extras/juce demo/src/demos/PathsAndTransformsDemo.cpp +++ b/extras/juce demo/src/demos/PathsAndTransformsDemo.cpp @@ -130,20 +130,18 @@ public: } else if (type == 2 || type == 3) { - GradientBrush gb (Colours::blue.withAlpha ((float) opacitySlider->getValue()), - getWidth() * 0.5f, getHeight() * 0.5f, - Colours::red.withAlpha ((float) opacitySlider->getValue()), - getWidth() * 0.6f, getHeight() * 0.7f, - type == 3); + ColourGradient gradient (Colours::blue.withAlpha ((float) opacitySlider->getValue()), + getWidth() * 0.5f, getHeight() * 0.5f, + Colours::red.withAlpha ((float) opacitySlider->getValue()), + getWidth() * 0.6f, getHeight() * 0.7f, + type == 3); - g.setBrush (&gb); + g.setGradientFill (gradient); g.fillPath (shape, getTransform()); } else if (type == 8) { - ImageBrush ib (image, 100, 100, (float) opacitySlider->getValue()); - - g.setBrush (&ib); + g.setTiledImageFill (*image, 100, 100, (float) opacitySlider->getValue()); g.fillPath (shape, getTransform()); } else if (type == 4 || type == 5) @@ -179,16 +177,16 @@ public: } else if (type == 7) { - GradientBrush gb (Colours::blue.withAlpha ((float) opacitySlider->getValue()), - getWidth() * 0.5f, getHeight() * 0.5f, - Colours::red.withAlpha ((float) opacitySlider->getValue()), - getWidth() * 0.6f, getHeight() * 0.7f, - false); - - g.setBrush (&gb); - if (image != 0) { + ColourGradient gradient (Colours::blue.withAlpha ((float) opacitySlider->getValue()), + getWidth() * 0.5f, getHeight() * 0.5f, + Colours::red.withAlpha ((float) opacitySlider->getValue()), + getWidth() * 0.6f, getHeight() * 0.7f, + false); + + g.setGradientFill (gradient); + g.drawImageTransformed (image, 0, 0, image->getWidth(), image->getHeight(), AffineTransform::translation (-0.5f * image->getWidth(), -0.5f * image->getHeight()) @@ -198,11 +196,10 @@ public: } else if (type == 9) { - ImageBrush ib (image, 100, 100, (float) opacitySlider->getValue()); - g.setBrush (&ib); - if (image != 0) { + g.setTiledImageFill (*image, 100, 100, (float) opacitySlider->getValue()); + g.drawImageTransformed (image, 0, 0, image->getWidth(), image->getHeight(), AffineTransform::translation (-0.5f * image->getWidth(), diff --git a/extras/the jucer/src/model/paintelements/jucer_FillType.cpp b/extras/the jucer/src/model/paintelements/jucer_FillType.cpp index f47e205463..5d22fa4a51 100644 --- a/extras/the jucer/src/model/paintelements/jucer_FillType.cpp +++ b/extras/the jucer/src/model/paintelements/jucer_FillType.cpp @@ -110,15 +110,14 @@ void FillType::reset() } //============================================================================== -Brush* FillType::createBrush (JucerDocument* const document, - const Rectangle& parentArea) +void FillType::setFillType (Graphics& g, JucerDocument* const document, const Rectangle& parentArea) { if (mode == solidColour) { ImageCache::release (image); image = 0; - return new SolidColourBrush (colour); + g.setColour (colour); } else if (mode == imageBrush) { @@ -126,7 +125,7 @@ Brush* FillType::createBrush (JucerDocument* const document, Rectangle r (imageAnchor.getRectangle (parentArea, document->getComponentLayout())); - return new ImageBrush (image, r.getX(), r.getY(), (float) imageOpacity); + g.setTiledImageFill (*image, r.getX(), r.getY(), (float) imageOpacity); } else { @@ -136,9 +135,9 @@ Brush* FillType::createBrush (JucerDocument* const document, Rectangle r1 (gradPos1.getRectangle (parentArea, document->getComponentLayout())); Rectangle r2 (gradPos2.getRectangle (parentArea, document->getComponentLayout())); - return new GradientBrush (gradCol1, (float) r1.getX(), (float) r1.getY(), - gradCol2, (float) r2.getX(), (float) r2.getY(), - mode == radialGradient); + g.setGradientFill (ColourGradient (gradCol1, (float) r1.getX(), (float) r1.getY(), + gradCol2, (float) r2.getX(), (float) r2.getY(), + mode == radialGradient)); } } diff --git a/extras/the jucer/src/model/paintelements/jucer_FillType.h b/extras/the jucer/src/model/paintelements/jucer_FillType.h index 86596bccdb..ed63d79462 100644 --- a/extras/the jucer/src/model/paintelements/jucer_FillType.h +++ b/extras/the jucer/src/model/paintelements/jucer_FillType.h @@ -46,7 +46,7 @@ public: bool operator!= (const FillType& other) const throw(); //============================================================================== - Brush* createBrush (JucerDocument* const document, const Rectangle& parentArea); + void setFillType (Graphics& g, JucerDocument* const document, const Rectangle& parentArea); void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) const; diff --git a/extras/the jucer/src/model/paintelements/jucer_PaintElementEllipse.h b/extras/the jucer/src/model/paintelements/jucer_PaintElementEllipse.h index c4db0187cd..3b283d9cb0 100644 --- a/extras/the jucer/src/model/paintelements/jucer_PaintElementEllipse.h +++ b/extras/the jucer/src/model/paintelements/jucer_PaintElementEllipse.h @@ -45,23 +45,17 @@ public: void draw (Graphics& g, const ComponentLayout* layout, const Rectangle& parentArea) { - Brush* const brush = fillType.createBrush (getDocument(), parentArea); - g.setBrush (brush); + fillType.setFillType (g, getDocument(), parentArea); Rectangle r (position.getRectangle (parentArea, layout)); g.fillEllipse ((float) r.getX(), (float) r.getY(), (float) r.getWidth(), (float) r.getHeight()); - delete brush; - if (isStrokePresent) { - Brush* const brush = strokeType.fill.createBrush (getDocument(), parentArea); - g.setBrush (brush); + strokeType.fill.setFillType (g, getDocument(), parentArea); g.drawEllipse ((float) r.getX(), (float) r.getY(), (float) r.getWidth(), (float) r.getHeight(), getStrokeType().stroke.getStrokeThickness()); - - delete brush; } } diff --git a/extras/the jucer/src/model/paintelements/jucer_PaintElementPath.cpp b/extras/the jucer/src/model/paintelements/jucer_PaintElementPath.cpp index 8fec3ab424..0cb065fd86 100644 --- a/extras/the jucer/src/model/paintelements/jucer_PaintElementPath.cpp +++ b/extras/the jucer/src/model/paintelements/jucer_PaintElementPath.cpp @@ -242,24 +242,16 @@ static void drawArrow (Graphics& g, float x1, float y1, float x2, float y2) void PaintElementPath::draw (Graphics& g, const ComponentLayout* layout, const Rectangle& parentArea) { - Brush* const brush = fillType.createBrush (getDocument(), parentArea); - g.setBrush (brush); - updateStoredPath (layout, parentArea); - path.setUsingNonZeroWinding (nonZeroWinding); - g.fillPath (path); - delete brush; + fillType.setFillType (g, getDocument(), parentArea); + g.fillPath (path); if (isStrokePresent) { - Brush* const brush = strokeType.fill.createBrush (getDocument(), parentArea); - g.setBrush (brush); - + strokeType.fill.setFillType (g, getDocument(), parentArea); g.strokePath (path, getStrokeType().stroke); - - delete brush; } } diff --git a/extras/the jucer/src/model/paintelements/jucer_PaintElementRectangle.h b/extras/the jucer/src/model/paintelements/jucer_PaintElementRectangle.h index 41ea520079..26d5828339 100644 --- a/extras/the jucer/src/model/paintelements/jucer_PaintElementRectangle.h +++ b/extras/the jucer/src/model/paintelements/jucer_PaintElementRectangle.h @@ -60,23 +60,17 @@ public: Component parentComponent; parentComponent.setBounds (parentArea); - Brush* const brush = fillType.createBrush (getDocument(), parentArea); - g.setBrush (brush); + fillType.setFillType (g, getDocument(), parentArea); const Rectangle r (position.getRectangle (parentArea, layout)); g.fillRect (r); - delete brush; - if (isStrokePresent) { - Brush* const brush = strokeType.fill.createBrush (getDocument(), parentArea); - g.setBrush (brush); + strokeType.fill.setFillType (g, getDocument(), parentArea); g.drawRect (r.getX(), r.getY(), r.getWidth(), r.getHeight(), roundDoubleToInt (getStrokeType().stroke.getStrokeThickness())); - - delete brush; } } diff --git a/extras/the jucer/src/model/paintelements/jucer_PaintElementRoundedRectangle.h b/extras/the jucer/src/model/paintelements/jucer_PaintElementRoundedRectangle.h index 9c23fdc789..b81c880f58 100644 --- a/extras/the jucer/src/model/paintelements/jucer_PaintElementRoundedRectangle.h +++ b/extras/the jucer/src/model/paintelements/jucer_PaintElementRoundedRectangle.h @@ -47,25 +47,18 @@ public: //============================================================================== void draw (Graphics& g, const ComponentLayout* layout, const Rectangle& parentArea) { - Brush* const brush = fillType.createBrush (getDocument(), parentArea); - g.setBrush (brush); - double x, y, w, h; position.getRectangleDouble (x, y, w, h, parentArea, layout); + fillType.setFillType (g, getDocument(), parentArea); g.fillRoundedRectangle ((float) x, (float) y, (float) w, (float) h, (float) cornerSize); - delete brush; - if (isStrokePresent) { - Brush* const brush = strokeType.fill.createBrush (getDocument(), parentArea); - g.setBrush (brush); + strokeType.fill.setFillType (g, getDocument(), parentArea); g.drawRoundedRectangle ((float) x, (float) y, (float) w, (float) h, (float) cornerSize, getStrokeType().stroke.getStrokeThickness()); - - delete brush; } } diff --git a/extras/the jucer/src/model/paintelements/jucer_PaintElementText.h b/extras/the jucer/src/model/paintelements/jucer_PaintElementText.h index 3f8b2f1778..1c8383790d 100644 --- a/extras/the jucer/src/model/paintelements/jucer_PaintElementText.h +++ b/extras/the jucer/src/model/paintelements/jucer_PaintElementText.h @@ -57,8 +57,7 @@ public: //============================================================================== void draw (Graphics& g, const ComponentLayout* layout, const Rectangle& parentArea) { - Brush* const brush = fillType.createBrush (getDocument(), parentArea); - g.setBrush (brush); + fillType.setFillType (g, getDocument(), parentArea); font = FontPropertyComponent::applyNameToFont (typefaceName, font); g.setFont (font); @@ -67,8 +66,6 @@ public: g.drawText (replaceStringTranslations (text, owner->getDocument()), r.getX(), r.getY(), r.getWidth(), r.getHeight(), justification, true); - - delete brush; } void getEditableProperties (Array & properties) diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index d9ec967e9b..fd958c5bed 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -730,7 +730,7 @@ public: the rest of the codebase. */ -#define USE_COREGRAPHICS_RENDERING 0 +#define USE_COREGRAPHICS_RENDERING 1 #if JUCE_IPHONE #import @@ -77527,408 +77527,6 @@ TopLevelWindow* TopLevelWindow::getActiveTopLevelWindow() throw() END_JUCE_NAMESPACE /********* End of inlined file: juce_TopLevelWindow.cpp *********/ -/********* Start of inlined file: juce_Brush.cpp *********/ - -BEGIN_JUCE_NAMESPACE - -Brush::Brush() throw() -{ -} - -Brush::~Brush() throw() -{ -} - -void Brush::paintVerticalLine (LowLevelGraphicsContext& context, - int x, float y1, float y2) throw() -{ - Path p; - p.addRectangle ((float) x, y1, 1.0f, y2 - y1); - paintPath (context, p, AffineTransform::identity); -} - -void Brush::paintHorizontalLine (LowLevelGraphicsContext& context, - int y, float x1, float x2) throw() -{ - Path p; - p.addRectangle (x1, (float) y, x2 - x1, 1.0f); - paintPath (context, p, AffineTransform::identity); -} - -void Brush::paintLine (LowLevelGraphicsContext& context, - float x1, float y1, float x2, float y2) throw() -{ - Path p; - p.addLineSegment (x1, y1, x2, y2, 1.0f); - paintPath (context, p, AffineTransform::identity); -} - -END_JUCE_NAMESPACE -/********* End of inlined file: juce_Brush.cpp *********/ - -/********* Start of inlined file: juce_GradientBrush.cpp *********/ - -BEGIN_JUCE_NAMESPACE - -GradientBrush::GradientBrush (const Colour& colour1, - const float x1, - const float y1, - const Colour& colour2, - const float x2, - const float y2, - const bool isRadial) throw() - : gradient (colour1, x1, y1, - colour2, x2, y2, - isRadial) -{ -} - -GradientBrush::GradientBrush (const ColourGradient& gradient_) throw() - : gradient (gradient_) -{ -} - -GradientBrush::~GradientBrush() throw() -{ -} - -Brush* GradientBrush::createCopy() const throw() -{ - return new GradientBrush (gradient); -} - -void GradientBrush::applyTransform (const AffineTransform& transform) throw() -{ - gradient.transform = gradient.transform.followedBy (transform); -} - -void GradientBrush::multiplyOpacity (const float multiple) throw() -{ - gradient.multiplyOpacity (multiple); -} - -bool GradientBrush::isInvisible() const throw() -{ - return gradient.isInvisible(); -} - -void GradientBrush::paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw() -{ - context.setGradient (gradient); - context.fillPath (path, transform); -} - -void GradientBrush::paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw() -{ - context.setGradient (gradient); - context.fillRect (x, y, w, h, false); -} - -void GradientBrush::paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw() -{ - context.saveState(); - - if (context.reduceClipRegion (x, y, w, h)) - { - context.setGradient (gradient); - context.fillAlphaChannel (alphaChannelImage, imageX, imageY); - } - - context.restoreState(); -} - -END_JUCE_NAMESPACE -/********* End of inlined file: juce_GradientBrush.cpp *********/ - -/********* Start of inlined file: juce_ImageBrush.cpp *********/ - -BEGIN_JUCE_NAMESPACE - -ImageBrush::ImageBrush (Image* const image_, - const int anchorX_, - const int anchorY_, - const float opacity_) throw() - : image (image_), - anchorX (anchorX_), - anchorY (anchorY_), - opacity (opacity_) -{ - jassert (image != 0); // not much point creating a brush without an image, is there? - - if (image != 0) - { - if (image->getWidth() == 0 || image->getHeight() == 0) - { - jassertfalse // you've passed in an empty image - not exactly brilliant for tiling. - image = 0; - } - } -} - -ImageBrush::~ImageBrush() throw() -{ -} - -Brush* ImageBrush::createCopy() const throw() -{ - return new ImageBrush (image, anchorX, anchorY, opacity); -} - -void ImageBrush::multiplyOpacity (const float multiple) throw() -{ - opacity *= multiple; -} - -bool ImageBrush::isInvisible() const throw() -{ - return opacity == 0.0f; -} - -void ImageBrush::applyTransform (const AffineTransform& /*transform*/) throw() -{ - //xxx should probably be smarter and warp the image -} - -void ImageBrush::getStartXY (int& x, int& y) const throw() -{ - x -= anchorX; - y -= anchorY; - - const int iw = image->getWidth(); - const int ih = image->getHeight(); - - if (x < 0) - x = ((x / iw) - 1) * iw; - else - x = (x / iw) * iw; - - if (y < 0) - y = ((y / ih) - 1) * ih; - else - y = (y / ih) * ih; - - x += anchorX; - y += anchorY; -} - -void ImageBrush::paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw() -{ - context.saveState(); - - if (image != 0 && context.reduceClipRegion (x, y, w, h)) - { - const int right = x + w; - const int bottom = y + h; - - const int iw = image->getWidth(); - const int ih = image->getHeight(); - - int startX = x; - getStartXY (startX, y); - - while (y < bottom) - { - x = startX; - - while (x < right) - { - context.setOpacity (opacity); - context.blendImage (*image, x, y, iw, ih, 0, 0); - x += iw; - } - - y += ih; - } - } - - context.restoreState(); -} - -void ImageBrush::paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw() -{ - if (image != 0) - { - Rectangle clip (context.getClipBounds()); - context.setOpacity (opacity); - - { - float x, y, w, h; - path.getBoundsTransformed (transform, x, y, w, h); - - clip = clip.getIntersection (Rectangle ((int) floorf (x), - (int) floorf (y), - 2 + (int) floorf (w), - 2 + (int) floorf (h))); - } - - int x = clip.getX(); - int y = clip.getY(); - const int right = clip.getRight(); - const int bottom = clip.getBottom(); - - const int iw = image->getWidth(); - const int ih = image->getHeight(); - - int startX = x; - getStartXY (startX, y); - - while (y < bottom) - { - x = startX; - - while (x < right) - { - context.fillPathWithImage (path, transform, *image, x, y); - x += iw; - } - - y += ih; - } - } -} - -void ImageBrush::paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw() -{ - context.saveState(); - - if (image != 0 && context.reduceClipRegion (x, y, w, h)) - { - context.setOpacity (opacity); - const Rectangle clip (context.getClipBounds()); - x = clip.getX(); - y = clip.getY(); - const int right = clip.getRight(); - const int bottom = clip.getBottom(); - - const int iw = image->getWidth(); - const int ih = image->getHeight(); - - int startX = x; - getStartXY (startX, y); - - while (y < bottom) - { - x = startX; - - while (x < right) - { - context.fillAlphaChannelWithImage (alphaChannelImage, - imageX, imageY, *image, - x, y); - x += iw; - } - - y += ih; - } - } - - context.restoreState(); -} - -END_JUCE_NAMESPACE -/********* End of inlined file: juce_ImageBrush.cpp *********/ - -/********* Start of inlined file: juce_SolidColourBrush.cpp *********/ - -BEGIN_JUCE_NAMESPACE - -SolidColourBrush::SolidColourBrush() throw() - : colour (0xff000000) -{ -} - -SolidColourBrush::SolidColourBrush (const Colour& colour_) throw() - : colour (colour_) -{ -} - -SolidColourBrush::~SolidColourBrush() throw() -{ -} - -Brush* SolidColourBrush::createCopy() const throw() -{ - return new SolidColourBrush (colour); -} - -void SolidColourBrush::applyTransform (const AffineTransform& /*transform*/) throw() -{ -} - -void SolidColourBrush::multiplyOpacity (const float multiple) throw() -{ - colour = colour.withMultipliedAlpha (multiple); -} - -bool SolidColourBrush::isInvisible() const throw() -{ - return colour.isTransparent(); -} - -void SolidColourBrush::paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw() -{ - context.setColour (colour); - context.fillPath (path, transform); -} - -void SolidColourBrush::paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw() -{ - context.setColour (colour); - context.fillRect (x, y, w, h, false); -} - -void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw() -{ - if (! colour.isTransparent()) - { - context.saveState(); - - if (context.reduceClipRegion (x, y, w, h)) - { - context.setColour (colour); - context.fillAlphaChannel (alphaChannelImage, imageX, imageY); - } - - context.restoreState(); - } -} - -void SolidColourBrush::paintVerticalLine (LowLevelGraphicsContext& context, - int x, float y1, float y2) throw() -{ - context.setColour (colour); - context.drawVerticalLine (x, y1, y2); -} - -void SolidColourBrush::paintHorizontalLine (LowLevelGraphicsContext& context, - int y, float x1, float x2) throw() -{ - context.setColour (colour); - context.drawHorizontalLine (y, x1, x2); -} - -void SolidColourBrush::paintLine (LowLevelGraphicsContext& context, - float x1, float y1, float x2, float y2) throw() -{ - context.setColour (colour); - context.drawLine (x1, y1, x2, y2); -} - -END_JUCE_NAMESPACE -/********* End of inlined file: juce_SolidColourBrush.cpp *********/ - /********* Start of inlined file: juce_Colour.cpp *********/ BEGIN_JUCE_NAMESPACE @@ -78196,12 +77794,18 @@ const Colour Colour::overlaidWith (const Colour& src) const throw() const Colour Colour::interpolatedWith (const Colour& other, float proportionOfOther) const throw() { - const int amount = jlimit (0, 256, (int) (proportionOfOther * 256.0f)); + if (proportionOfOther <= 0) + return *this; - return Colour ((uint8) (getRed() + (((other.getRed() - getRed()) * amount) >> 8)), - (uint8) (getGreen() + (((other.getGreen() - getGreen()) * amount) >> 8)), - (uint8) (getBlue() + (((other.getBlue() - getBlue()) * amount) >> 8)), - (uint8) (getAlpha() + (((other.getAlpha() - getAlpha()) * amount) >> 8))); + if (proportionOfOther >= 1.0f) + return other; + + PixelARGB c1 (getPixelARGB()); + const PixelARGB c2 (other.getPixelARGB()); + c1.tween (c2, roundFloatToInt (proportionOfOther * 255.0f)); + c1.unpremultiply(); + + return Colour (c1.getARGB()); } float Colour::getFloatRed() const throw() @@ -78929,7 +78533,8 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, const Path& path, const AffineTransform& transform) throw() : bounds (bounds_), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + needToCheckEmptinesss (true) { table = (int*) juce_malloc ((bounds.getHeight() + 1) * lineStrideElements * sizeof (int)); int* t = table; @@ -79047,7 +78652,8 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) throw() : bounds (rectangleToAdd), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + needToCheckEmptinesss (true) { table = (int*) juce_malloc (jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int)); *table = 0; @@ -79070,7 +78676,8 @@ EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) throw() EdgeTable::EdgeTable (const float x, const float y, const float w, const float h) throw() : bounds (Rectangle ((int) floorf (x), roundFloatToInt (y * 256.0f) >> 8, 2 + (int) w, 2 + (int) h)), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + needToCheckEmptinesss (true) { jassert (w > 0 && h > 0); table = (int*) juce_malloc (jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int)); @@ -79154,6 +78761,7 @@ const EdgeTable& EdgeTable::operator= (const EdgeTable& other) throw() bounds = other.bounds; maxEdgesPerLine = other.maxEdgesPerLine; lineStrideElements = other.lineStrideElements; + needToCheckEmptinesss = other.needToCheckEmptinesss; const int tableSize = jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int); table = (int*) juce_malloc (tableSize); @@ -79237,6 +78845,27 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw line[0]++; } +void EdgeTable::translate (float dx, int dy) throw() +{ + bounds.setPosition (bounds.getX() + (int) floorf (dx), bounds.getY() + dy); + + int* lineStart = table; + const int intDx = (int) (dx * 256.0f); + + for (int i = bounds.getHeight(); --i >= 0;) + { + int* line = lineStart; + lineStart += lineStrideElements; + int num = *line++; + + while (--num >= 0) + { + *line += intDx; + line += 2; + } + } +} + void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) throw() { jassert (y >= 0 && y < bounds.getHeight()); @@ -79360,6 +78989,7 @@ void EdgeTable::clipToRectangle (const Rectangle& r) throw() if (clipped.isEmpty()) { + needToCheckEmptinesss = false; bounds.setHeight (0); } else @@ -79383,6 +79013,8 @@ void EdgeTable::clipToRectangle (const Rectangle& r) throw() for (int i = top; i < bottom; ++i) intersectWithEdgeTableLine (i, rectLine); } + + needToCheckEmptinesss = true; } } @@ -79401,6 +79033,8 @@ void EdgeTable::excludeRectangle (const Rectangle& r) throw() for (int i = top; i < bottom; ++i) intersectWithEdgeTableLine (i, rectLine); + + needToCheckEmptinesss = true; } } @@ -79410,6 +79044,7 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) if (clipped.isEmpty()) { + needToCheckEmptinesss = false; bounds.setHeight (0); } else @@ -79433,109 +79068,74 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) intersectWithEdgeTableLine (i, otherLine); otherLine += other.lineStrideElements; } + + needToCheckEmptinesss = true; } } -void EdgeTable::clipToImageAlpha (const Image& image, int x, int y) throw() +void EdgeTable::clipLineToMask (int x, int y, uint8* mask, int maskStride, int numPixels) throw() { - const Rectangle clipped (bounds.getIntersection (Rectangle (x, y, image.getWidth(), image.getHeight()))); + y -= bounds.getY(); - if (clipped.isEmpty()) + if (y < 0 || y >= bounds.getHeight()) + return; + + needToCheckEmptinesss = true; + + if (numPixels <= 0) { + table [lineStrideElements * y] = 0; + return; + } + + int* tempLine = (int*) alloca ((numPixels * 2 + 4) * sizeof (int)); + int destIndex = 0, lastLevel = 0; + + while (--numPixels >= 0) + { + const int alpha = *mask; + mask += maskStride; + + if (alpha != lastLevel) + { + tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = alpha; + lastLevel = alpha; + } + + ++x; + } + + if (lastLevel > 0) + { + tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = 0; + } + + tempLine[0] = destIndex >> 1; + + intersectWithEdgeTableLine (y, tempLine); +} + +bool EdgeTable::isEmpty() throw() +{ + if (needToCheckEmptinesss) + { + needToCheckEmptinesss = false; + int* t = table; + + for (int i = bounds.getHeight(); --i >= 0;) + { + if (t[0] > 1) + return false; + + t += lineStrideElements; + } + bounds.setHeight (0); } - else - { - if (! image.hasAlphaChannel()) - { - clipToRectangle (clipped); - return; - } - const int top = clipped.getY() - bounds.getY(); - const int bottom = clipped.getBottom() - bounds.getY(); - - if (bottom < bounds.getHeight()) - bounds.setHeight (bottom); - - if (clipped.getRight() < bounds.getRight()) - bounds.setRight (clipped.getRight()); - - for (int i = top; --i >= 0;) - table [lineStrideElements * i] = 0; - - int imageX = clipped.getX() - x; - int imageY = clipped.getY() - y; - - int* temp = (int*) alloca ((clipped.getWidth() * 2 + 4) * sizeof (int)); - - Image::BitmapData srcData (image, imageX, imageY, clipped.getWidth(), clipped.getHeight()); - - for (int i = 0; i < clipped.getHeight(); ++i) - { - int destIndex = 0, lastLevel = 0; - const uint8* pixels = srcData.getLinePointer (i); - - if (image.getFormat() == Image::ARGB) - { - for (int px = 0; px < clipped.getWidth(); ++px) - { - const int alpha = ((const PixelARGB*) pixels)->getAlpha(); - pixels += srcData.pixelStride; - - if (alpha != lastLevel) - { - temp[++destIndex] = (clipped.getX() + px) << 8; - temp[++destIndex] = alpha; - lastLevel = alpha; - } - } - } - else - { - jassert (image.getFormat() == Image::SingleChannel); - - for (int px = 0; px < clipped.getWidth(); ++px) - { - const int alpha = *pixels; - pixels += srcData.pixelStride; - - if (alpha != lastLevel) - { - temp[++destIndex] = (clipped.getX() + px) << 8; - temp[++destIndex] = alpha; - lastLevel = alpha; - } - } - } - - if (lastLevel > 0) - { - temp[++destIndex] = clipped.getRight() << 8; - temp[++destIndex] = 0; - } - - temp[0] = destIndex >> 1; - - intersectWithEdgeTableLine (top + i, temp); - ++y; - } - } -} - -bool EdgeTable::isEmpty() const throw() -{ - int* t = table; - - for (int i = bounds.getHeight(); --i >= 0;) - { - if (t[0] > 1) - return false; - - t += lineStrideElements; - } - - return true; + return bounds.getHeight() == 0; } END_JUCE_NAMESPACE @@ -79572,7 +79172,6 @@ LowLevelGraphicsContext::~LowLevelGraphicsContext() Graphics::Graphics (Image& imageToDrawOnto) throw() : context (imageToDrawOnto.createLowLevelContext()), ownsContext (true), - state (new GraphicsState()), saveStatePending (false) { resetToDefaultState(); @@ -79581,7 +79180,6 @@ Graphics::Graphics (Image& imageToDrawOnto) throw() Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() : context (internalContext), ownsContext (false), - state (new GraphicsState()), saveStatePending (false) { resetToDefaultState(); @@ -79589,8 +79187,6 @@ Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() Graphics::~Graphics() throw() { - delete state; - if (ownsContext) delete context; } @@ -79612,20 +79208,20 @@ bool Graphics::reduceClipRegion (const int x, const int y, const int w, const int h) throw() { saveStateIfPending(); - return context->reduceClipRegion (x, y, w, h); + return context->clipToRectangle (Rectangle (x, y, w, h)); } bool Graphics::reduceClipRegion (const RectangleList& clipRegion) throw() { saveStateIfPending(); - return context->reduceClipRegion (clipRegion); + return context->clipToRectangleList (clipRegion); } void Graphics::excludeClipRegion (const int x, const int y, const int w, const int h) throw() { saveStateIfPending(); - context->excludeClipRegion (x, y, w, h); + context->excludeClipRectangle (Rectangle (x, y, w, h)); } bool Graphics::isClipEmpty() const throw() @@ -79647,29 +79243,9 @@ void Graphics::saveState() throw() void Graphics::restoreState() throw() { if (saveStatePending) - { saveStatePending = false; - } else - { - const int stackSize = stateStack.size(); - - if (stackSize > 0) - { - context->restoreState(); - - delete state; - state = stateStack.getUnchecked (stackSize - 1); - - stateStack.removeLast (1, false); - } - else - { - // Trying to call restoreState() more times than you've called saveState() ! - // Be careful to correctly match each saveState() with exactly one call to restoreState(). - jassertfalse - } - } + context->restoreState(); } void Graphics::saveStateIfPending() throw() @@ -79677,9 +79253,7 @@ void Graphics::saveStateIfPending() throw() if (saveStatePending) { saveStatePending = false; - context->saveState(); - stateStack.add (new GraphicsState (*state)); } } @@ -79693,14 +79267,12 @@ void Graphics::setOrigin (const int newOriginX, bool Graphics::clipRegionIntersects (const int x, const int y, const int w, const int h) const throw() { - return context->clipRegionIntersects (x, y, w, h); + return context->clipRegionIntersects (Rectangle (x, y, w, h)); } void Graphics::setColour (const Colour& newColour) throw() { saveStateIfPending(); - - deleteAndZero (state->brush); context->setColour (newColour); } @@ -79710,73 +79282,25 @@ void Graphics::setOpacity (const float newOpacity) throw() context->setOpacity (newOpacity); } -void Graphics::setBrush (const Brush* const newBrush) throw() -{ - saveStateIfPending(); - delete state->brush; - state->brush = 0; - - if (newBrush != 0) - { - const SolidColourBrush* cb = dynamic_cast (newBrush); - - if (cb != 0) - { - setColour (cb->getColour()); - } - else - { - const GradientBrush* gb = dynamic_cast (newBrush); - - if (gb != 0) - { - setGradientFill (gb->getGradient()); - } - else - { - state->brush = newBrush->createCopy(); - } - } - } -} - void Graphics::setGradientFill (const ColourGradient& gradient) throw() { saveStateIfPending(); - deleteAndZero (state->brush); context->setGradient (gradient); } -void Graphics::setTiledImageFill (Image& imageToUse, +void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity) throw() { saveStateIfPending(); - delete state->brush; - state->brush = new ImageBrush (&imageToUse, anchorX, anchorY, opacity); -} - -Graphics::GraphicsState::GraphicsState() throw() - : brush (0) -{ -} - -Graphics::GraphicsState::GraphicsState (const GraphicsState& other) throw() - : brush (other.brush != 0 ? other.brush->createCopy() : 0), - font (other.font) -{ -} - -Graphics::GraphicsState::~GraphicsState() throw() -{ - delete brush; + context->setTiledFill (imageToUse, anchorX, anchorY); + context->setOpacity (opacity); } void Graphics::setFont (const Font& newFont) throw() { saveStateIfPending(); - state->font = newFont; context->setFont (newFont); } @@ -79784,8 +79308,9 @@ void Graphics::setFont (const float newFontHeight, const int newFontStyleFlags) throw() { saveStateIfPending(); - state->font.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0); - context->setFont (state->font); + Font f (context->getFont()); + f.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0); + context->setFont (f); } void Graphics::drawSingleLineText (const String& text, @@ -79796,7 +79321,7 @@ void Graphics::drawSingleLineText (const String& text, && startX < context->getClipBounds().getRight()) { GlyphArrangement arr; - arr.addLineOfText (state->font, text, (float) startX, (float) baselineY); + arr.addLineOfText (context->getFont(), text, (float) startX, (float) baselineY); arr.draw (*this); } } @@ -79807,7 +79332,7 @@ void Graphics::drawTextAsPath (const String& text, if (text.isNotEmpty()) { GlyphArrangement arr; - arr.addLineOfText (state->font, text, 0.0f, 0.0f); + arr.addLineOfText (context->getFont(), text, 0.0f, 0.0f); arr.draw (*this, transform); } } @@ -79821,7 +79346,7 @@ void Graphics::drawMultiLineText (const String& text, && startX < context->getClipBounds().getRight()) { GlyphArrangement arr; - arr.addJustifiedText (state->font, text, + arr.addJustifiedText (context->getFont(), text, (float) startX, (float) baselineY, (float) maximumLineWidth, Justification::left); arr.draw (*this); @@ -79836,11 +79361,11 @@ void Graphics::drawText (const String& text, const Justification& justificationType, const bool useEllipsesIfTooBig) const throw() { - if (text.isNotEmpty() && context->clipRegionIntersects (x, y, width, height)) + if (text.isNotEmpty() && context->clipRegionIntersects (Rectangle (x, y, width, height))) { GlyphArrangement arr; - arr.addCurtailedLineOfText (state->font, text, + arr.addCurtailedLineOfText (context->getFont(), text, 0.0f, 0.0f, (float)width, useEllipsesIfTooBig); @@ -79863,11 +79388,11 @@ void Graphics::drawFittedText (const String& text, { if (text.isNotEmpty() && width > 0 && height > 0 - && context->clipRegionIntersects (x, y, width, height)) + && context->clipRegionIntersects (Rectangle (x, y, width, height))) { GlyphArrangement arr; - arr.addFittedText (state->font, text, + arr.addFittedText (context->getFont(), text, (float) x, (float) y, (float) width, (float) height, justification, @@ -79886,18 +79411,12 @@ void Graphics::fillRect (int x, // passing in a silly number can cause maths problems in rendering! ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); - if (state->brush == 0) - context->fillRect (x, y, width, height, false); - else - state->brush->paintRectangle (*context, x, y, width, height); + context->fillRect (Rectangle (x, y, width, height), false); } void Graphics::fillRect (const Rectangle& r) const throw() { - fillRect (r.getX(), - r.getY(), - r.getWidth(), - r.getHeight()); + context->fillRect (r, false); } void Graphics::fillRect (const float x, @@ -79915,13 +79434,7 @@ void Graphics::fillRect (const float x, void Graphics::setPixel (int x, int y) const throw() { - if (context->clipRegionIntersects (x, y, 1, 1)) - { - if (state->brush == 0) - context->fillRect (x, y, 1, 1, false); - else - state->brush->paintRectangle (*context, x, y, 1, 1); - } + context->fillRect (Rectangle (x, y, 1, 1), false); } void Graphics::fillAll() const throw() @@ -79937,7 +79450,7 @@ void Graphics::fillAll (const Colour& colourToUse) const throw() context->saveState(); context->setColour (colourToUse); - context->fillRect (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(), false); + context->fillRect (clip, false); context->restoreState(); } } @@ -79946,24 +79459,16 @@ void Graphics::fillPath (const Path& path, const AffineTransform& transform) const throw() { if ((! context->isClipEmpty()) && ! path.isEmpty()) - { - if (state->brush == 0) - context->fillPath (path, transform); - else - state->brush->paintPath (*context, path, transform); - } + context->fillPath (path, transform); } void Graphics::strokePath (const Path& path, const PathStrokeType& strokeType, const AffineTransform& transform) const throw() { -// if ((! state->colour.isTransparent()) || state->brush != 0) - { - Path stroke; - strokeType.createStrokedPath (stroke, path, transform); - fillPath (stroke); - } + Path stroke; + strokeType.createStrokedPath (stroke, path, transform); + fillPath (stroke); } void Graphics::drawRect (const int x, @@ -79975,21 +79480,10 @@ void Graphics::drawRect (const int x, // passing in a silly number can cause maths problems in rendering! ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); - if (state->brush == 0) - { - context->fillRect (x, y, width, lineThickness, false); - context->fillRect (x, y + lineThickness, lineThickness, height - lineThickness * 2, false); - context->fillRect (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2, false); - context->fillRect (x, y + height - lineThickness, width, lineThickness, false); - } - else - { - Brush& b = *(state->brush); - b.paintRectangle (*context, x, y, width, lineThickness); - b.paintRectangle (*context, x, y + lineThickness, lineThickness, height - lineThickness * 2); - b.paintRectangle (*context, x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2); - b.paintRectangle (*context, x, y + height - lineThickness, width, lineThickness); - } + context->fillRect (Rectangle (x, y, width, lineThickness), false); + context->fillRect (Rectangle (x, y + lineThickness, lineThickness, height - lineThickness * 2), false); + context->fillRect (Rectangle (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2), false); + context->fillRect (Rectangle (x, y + height - lineThickness, width, lineThickness), false); } void Graphics::drawRect (const float x, @@ -80043,13 +79537,13 @@ void Graphics::drawBevel (const int x, : oldOpacity; context->setColour (topLeftColour.withMultipliedAlpha (op)); - context->fillRect (x + i, y + i, width - i * 2, 1, false); + context->fillRect (Rectangle (x + i, y + i, width - i * 2, 1), false); context->setColour (topLeftColour.withMultipliedAlpha (op * 0.75f)); - context->fillRect (x + i, y + i + 1, 1, height - i * 2 - 2, false); + context->fillRect (Rectangle (x + i, y + i + 1, 1, height - i * 2 - 2), false); context->setColour (bottomRightColour.withMultipliedAlpha (op)); - context->fillRect (x + i, y + height - i - 1, width - i * 2, 1, false); + context->fillRect (Rectangle (x + i, y + height - i - 1, width - i * 2, 1), false); context->setColour (bottomRightColour.withMultipliedAlpha (op * 0.75f)); - context->fillRect (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2, false); + context->fillRect (Rectangle (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2), false); } context->restoreState(); @@ -80163,7 +79657,7 @@ void Graphics::fillCheckerBoard (int x, int y, if (colour1 == colour2) { context->setColour (colour1); - context->fillRect (x, y, width, height, false); + context->fillRect (Rectangle (x, y, width, height), false); } else { @@ -80180,9 +79674,7 @@ void Graphics::fillCheckerBoard (int x, int y, for (int xx = x; xx < right; xx += checkWidth) { context->setColour (((cx++ & 1) == 0) ? colour1 : colour2); - context->fillRect (xx, y, - jmin (checkWidth, right - xx), - jmin (checkHeight, bottom - y), + context->fillRect (Rectangle (xx, y, jmin (checkWidth, right - xx), jmin (checkHeight, bottom - y)), false); } @@ -80197,30 +79689,17 @@ void Graphics::fillCheckerBoard (int x, int y, void Graphics::drawVerticalLine (const int x, float top, float bottom) const throw() { - if (state->brush == 0) - context->drawVerticalLine (x, top, bottom); - else - state->brush->paintVerticalLine (*context, x, top, bottom); + context->drawVerticalLine (x, top, bottom); } void Graphics::drawHorizontalLine (const int y, float left, float right) const throw() { - if (state->brush == 0) - context->drawHorizontalLine (y, left, right); - else - state->brush->paintHorizontalLine (*context, y, left, right); + context->drawHorizontalLine (y, left, right); } -void Graphics::drawLine (float x1, float y1, - float x2, float y2) const throw() +void Graphics::drawLine (float x1, float y1, float x2, float y2) const throw() { - if (! context->isClipEmpty()) - { - if (state->brush == 0) - context->drawLine (x1, y1, x2, y2); - else - state->brush->paintLine (*context, x1, y1, x2, y2); - } + context->drawLine (x1, y1, x2, y2); } void Graphics::drawLine (const float startX, @@ -80357,126 +79836,14 @@ void Graphics::drawImage (const Image* const imageToDraw, ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (dx, dy, dw, dh); ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (sx, sy, sw, sh); - if (imageToDraw == 0 || ! context->clipRegionIntersects (dx, dy, dw, dh)) + if (! context->clipRegionIntersects (Rectangle (dx, dy, dw, dh))) return; - if (sw == dw && sh == dh) - { - if (sx < 0) - { - dx -= sx; - dw += sx; - sw += sx; - sx = 0; - } - - if (sx + sw > imageToDraw->getWidth()) - { - const int amount = sx + sw - imageToDraw->getWidth(); - dw -= amount; - sw -= amount; - } - - if (sy < 0) - { - dy -= sy; - dh += sy; - sh += sy; - sy = 0; - } - - if (sy + sh > imageToDraw->getHeight()) - { - const int amount = sy + sh - imageToDraw->getHeight(); - dh -= amount; - sh -= amount; - } - - if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) - return; - - if (fillAlphaChannelWithCurrentBrush) - { - if (state->brush == 0) - { - context->saveState(); - - if (context->reduceClipRegion (dx, dy, dw, dh)) - context->fillAlphaChannel (*imageToDraw, dx - sx, dy - sy); - - context->restoreState(); - } - else - { - state->brush->paintAlphaChannel (*context, *imageToDraw, - dx - sx, dy - sy, - dx, dy, dw, dh); - } - } - else - { - context->blendImage (*imageToDraw, - dx, dy, dw, dh, sx, sy); - } - } - else - { - if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) - return; - - if (fillAlphaChannelWithCurrentBrush) - { - if (imageToDraw->isRGB()) - { - fillRect (dx, dy, dw, dh); - } - else - { - int tx = dx; - int ty = dy; - int tw = dw; - int th = dh; - - if (context->getClipBounds().intersectRectangle (tx, ty, tw, th)) - { - Image temp (imageToDraw->getFormat(), tw, th, true); - Graphics g (temp); -//xxx g.setImageResamplingQuality (state->quality); - g.setOrigin (dx - tx, dy - ty); - - g.drawImage (imageToDraw, - 0, 0, dw, dh, - sx, sy, sw, sh, - false); - - if (state->brush == 0) - { - context->saveState(); - - if (context->reduceClipRegion (tx, ty, tw, th)) - context->fillAlphaChannel (temp, tx, ty); - - context->restoreState(); - } - else - { - state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); - } - } - } - } - else - { - context->blendImageWarping (*imageToDraw, - sx, sy, sw, sh, - AffineTransform::translation ((float) -sx, - (float) -sy) - .scaled (dw / (float) sw, - dh / (float) sh) - .translated ((float) dx, - (float) dy)); - } - } + drawImageTransformed (imageToDraw, sx, sy, sw, sh, + AffineTransform::translation ((float) -sx, (float) -sy) + .scaled (dw / (float) sw, dh / (float) sh) + .translated ((float) dx, (float) dy), + fillAlphaChannelWithCurrentBrush); } void Graphics::drawImageTransformed (const Image* const imageToDraw, @@ -80487,73 +79854,97 @@ void Graphics::drawImageTransformed (const Image* const imageToDraw, const AffineTransform& transform, const bool fillAlphaChannelWithCurrentBrush) const throw() { - if (imageToDraw != 0 - && (! context->isClipEmpty()) - && ! transform.isSingularity()) + if (imageToDraw != 0 && ! context->isClipEmpty()) { - if (transform.isIdentity()) + const Rectangle srcClip (Rectangle (sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight)); + + if (fillAlphaChannelWithCurrentBrush) { - drawImage (imageToDraw, - sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight, - sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight, - fillAlphaChannelWithCurrentBrush); - } - else if (fillAlphaChannelWithCurrentBrush) - { - Path p; - p.addRectangle ((float) sourceClipX, (float) sourceClipY, - (float) sourceClipWidth, (float) sourceClipHeight); - - p.applyTransform (transform); - - float dx, dy, dw, dh; - p.getBounds (dx, dy, dw, dh); - int tx = (int) dx; - int ty = (int) dy; - int tw = roundFloatToInt (dw) + 2; - int th = roundFloatToInt (dh) + 2; - - if (context->getClipBounds().intersectRectangle (tx, ty, tw, th)) - { - Image temp (imageToDraw->getFormat(), tw, th, true); - Graphics g (temp); -//xxx g.setImageResamplingQuality (state->quality); - - g.drawImageTransformed (imageToDraw, - sourceClipX, - sourceClipY, - sourceClipWidth, - sourceClipHeight, - transform.translated ((float) -tx, (float) -ty), - false); - - if (state->brush == 0) - { - context->saveState(); - - if (context->reduceClipRegion (tx, ty, tw, th)) - context->fillAlphaChannel (temp, tx, ty); - - context->restoreState(); - } - else - { - state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); - } - } + context->saveState(); + context->clipToImageAlpha (*imageToDraw, srcClip, transform); + fillAll(); + context->restoreState(); } else { - context->blendImageWarping (*imageToDraw, - sourceClipX, - sourceClipY, - sourceClipWidth, - sourceClipHeight, - transform); + context->drawImage (*imageToDraw, srcClip, transform, false); } } } +Graphics::FillType::FillType() throw() + : colour (0xff000000), gradient (0), + image (0), imageX (0), imageY (0) +{ +} + +Graphics::FillType::FillType (const Colour& colour_) throw() + : colour (colour_), gradient (0), + image (0), imageX (0), imageY (0) +{ +} + +Graphics::FillType::FillType (const ColourGradient& gradient) throw() + : colour (0xff000000), gradient (new ColourGradient (gradient)), + image (0), imageX (0), imageY (0) +{ +} + +Graphics::FillType::FillType (Image* const image_, const int x, const int y) throw() + : colour (0xff000000), gradient (0), + image (image_), imageX (x), imageY (y) +{ +} + +Graphics::FillType::FillType (const FillType& other) throw() + : colour (other.colour), + gradient (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0), + image (other.image), imageX (other.imageX), imageY (other.imageY) +{ +} + +const Graphics::FillType& Graphics::FillType::operator= (const FillType& other) throw() +{ + if (this != &other) + { + colour = other.colour; + delete gradient; + gradient = (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0); + image = other.image; + imageX = other.imageX; + imageY = other.imageY; + } + + return *this; +} + +Graphics::FillType::~FillType() throw() +{ + delete gradient; +} + +void Graphics::FillType::setColour (const Colour& newColour) throw() +{ + deleteAndZero (gradient); + colour = newColour; +} + +void Graphics::FillType::setGradient (const ColourGradient& newGradient) throw() +{ + if (gradient != 0) + *gradient = newGradient; + else + gradient = new ColourGradient (newGradient); +} + +void Graphics::FillType::setTiledImage (const Image& image_, const int imageX_, const int imageY_) throw() +{ + deleteAndZero (gradient); + image = &image_; + imageX = imageX_; + imageY = imageY_; +} + END_JUCE_NAMESPACE /********* End of inlined file: juce_Graphics.cpp *********/ @@ -81182,7 +80573,10 @@ void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, float x, fl void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) { - font.renderGlyphIndirectly (*this, glyphNumber, transform); + Path p; + font.getTypeface()->getOutlineForGlyph (glyphNumber, p); + + fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform)); } END_JUCE_NAMESPACE @@ -81205,150 +80599,6 @@ BEGIN_JUCE_NAMESPACE #pragma warning (disable: 4127) // "expression is constant" warning #endif -/*static void replaceRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() -{ - const PixelARGB blendColour (colour.getPixelARGB()); - - while (--h >= 0) - { - PixelARGB* const dest = (PixelARGB*) pixels; - - for (int i = 0; i < w; ++i) - dest[i] = blendColour; - - pixels += stride; - } -} - -static void blendRectRGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() -{ - if (colour.isOpaque()) - { - replaceRectRGB (pixels, w, h, stride, colour); - } - else - { - const PixelARGB blendColour (colour.getPixelARGB()); - const int alpha = blendColour.getAlpha(); - - if (alpha <= 0) - return; - -#if JUCE_USE_SSE_INSTRUCTIONS - if (SystemStats::hasSSE()) - { - int64 rgb0 = (((int64) blendColour.getRed()) << 32) - | (int64) ((blendColour.getGreen() << 16) - | blendColour.getBlue()); - - const int invAlpha = 0xff - alpha; - int64 aaaa = (invAlpha << 16) | invAlpha; - aaaa = (aaaa << 16) | aaaa; - -#ifndef JUCE_GCC - __asm - { - movq mm1, aaaa - movq mm2, rgb0 - pxor mm7, mm7 - } - - while (--h >= 0) - { - __asm - { - mov edx, pixels - mov ebx, w - - pixloop: - prefetchnta [edx] - mov ax, [edx + 1] - shl eax, 8 - mov al, [edx] - movd mm0, eax - - punpcklbw mm0, mm7 - pmullw mm0, mm1 - psrlw mm0, 8 - paddw mm0, mm2 - packuswb mm0, mm7 - - movd eax, mm0 - mov [edx], al - inc edx - shr eax, 8 - mov [edx], ax - add edx, 2 - - dec ebx - jg pixloop - } - - pixels += stride; - } - - __asm emms -#else - __asm__ __volatile__ ( - "\tpush %%ebx \n" - "\tmovq %[aaaa], %%mm1 \n" - "\tmovq %[rgb0], %%mm2 \n" - "\tpxor %%mm7, %%mm7 \n" - ".lineLoop2: \n" - "\tmovl %%esi,%%edx \n" - "\tmovl %[w], %%ebx \n" - ".pixLoop2: \n" - "\tprefetchnta (%%edx) \n" - "\tmov (%%edx), %%ax \n" - "\tshl $8, %%eax \n" - "\tmov 2(%%edx), %%al \n" - "\tmovd %%eax, %%mm0 \n" - "\tpunpcklbw %%mm7, %%mm0 \n" - "\tpmullw %%mm1, %%mm0 \n" - "\tpsrlw $8, %%mm0 \n" - "\tpaddw %%mm2, %%mm0 \n" - "\tpackuswb %%mm7, %%mm0 \n" - "\tmovd %%mm0, %%eax \n" - "\tmovb %%al, (%%edx) \n" - "\tinc %%edx \n" - "\tshr $8, %%eax \n" - "\tmovw %%ax, (%%edx) \n" - "\tadd $2, %%edx \n" - "\tdec %%ebx \n" - "\tjg .pixLoop2 \n" - "\tadd %%edi, %%esi \n" - "\tdec %%ecx \n" - "\tjg .lineLoop2 \n" - "\tpop %%ebx \n" - "\temms \n" - : // No output registers - : [aaaa] "m" (aaaa), // Input registers - [rgb0] "m" (rgb0), - [w] "m" (w), - "c" (h), - [stride] "D" (stride), - [pixels] "S" (pixels) - : "cc", "eax", "edx", "memory" - ); -#endif - } - else -#endif - { - while (--h >= 0) - { - PixelRGB* dest = (PixelRGB*) pixels; - - for (int i = w; --i >= 0;) - (dest++)->blend (blendColour); - - pixels += stride; - } - } - } -} -*/ - template class SolidColourEdgeTableRenderer { @@ -81470,10 +80720,8 @@ private: class LinearGradientPixelGenerator { public: - LinearGradientPixelGenerator (const ColourGradient& gradient, - const PixelARGB* const lookupTable_, const int numEntries_) - : lookupTable (lookupTable_), - numEntries (numEntries_) + LinearGradientPixelGenerator (const ColourGradient& gradient, const PixelARGB* const lookupTable_, const int numEntries_) + : lookupTable (lookupTable_), numEntries (numEntries_) { jassert (numEntries_ >= 0); float x1 = gradient.x1; @@ -81483,7 +80731,7 @@ public: if (! gradient.transform.isIdentity()) { - Line l (x2, y2, x1, y1); + const Line l (x2, y2, x1, y1); const Point p3 = l.getPointAlongLine (0.0, 100.0f); float x3 = p3.getX(); float y3 = p3.getY(); @@ -81492,8 +80740,8 @@ public: gradient.transform.transformPoint (x2, y2); gradient.transform.transformPoint (x3, y3); - Line l2 (x2, y2, x3, y3); - float prop = l2.findNearestPointTo (x1, y1); + const Line l2 (x2, y2, x3, y3); + const float prop = l2.findNearestPointTo (x1, y1); const Point newP2 (l2.getPointAlongLineProportionally (prop)); x2 = newP2.getX(); @@ -81532,10 +80780,8 @@ public: forcedinline const PixelARGB getPixel (const int x) const throw() { - if (vertical) - return linePix; - - return lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; + return vertical ? linePix + : lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; } private: @@ -81565,7 +80811,8 @@ public: const float dx = gradient.x1 - gradient.x2; const float dy = gradient.y1 - gradient.y2; maxDist = dx * dx + dy * dy; - invScale = (numEntries + 1) / sqrt (maxDist); + invScale = numEntries / sqrt (maxDist); + jassert (roundDoubleToInt (sqrt (maxDist) * invScale) <= numEntries); } forcedinline void setY (const int y) throw() @@ -81580,10 +80827,7 @@ public: x *= x; x += dy; - if (x >= maxDist) - return lookupTable [numEntries]; - else - return lookupTable [jmin (numEntries, roundDoubleToInt (sqrt (x) * invScale))]; + return lookupTable [x >= maxDist ? numEntries : roundDoubleToInt (sqrt (x) * invScale)]; } protected: @@ -81688,55 +80932,97 @@ private: const GradientEdgeTableRenderer& operator= (const GradientEdgeTableRenderer&); }; -template +static forcedinline int safeModulo (int n, const int divisor) throw() +{ + jassert (divisor > 0); + n %= divisor; + return (n < 0) ? (n + divisor) : n; +} + +template class ImageFillEdgeTableRenderer { public: ImageFillEdgeTableRenderer (const Image::BitmapData& destData_, const Image::BitmapData& srcData_, - const int extraAlpha_) throw() + const int extraAlpha_, + const int x, const int y) throw() : destData (destData_), srcData (srcData_), - extraAlpha (extraAlpha_ + 1) + extraAlpha (extraAlpha_ + 1), + xOffset (repeatPattern ? safeModulo (x, srcData_.width) - srcData_.width : x), + yOffset (repeatPattern ? safeModulo (y, srcData_.height) - srcData_.height : y) { } forcedinline void setEdgeTableYPos (int y) throw() { linePixels = (DestPixelType*) destData.getLinePointer (y); + + y -= yOffset; + if (repeatPattern) + { + jassert (y >= 0); + y %= srcData.height; + } + sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y); } - forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const throw() + forcedinline void handleEdgeTablePixel (int x, int alphaLevel) const throw() { alphaLevel = (alphaLevel * extraAlpha) >> 8; - linePixels[x].blend (sourceLineStart [x], alphaLevel); + linePixels[x].blend (sourceLineStart [repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)], alphaLevel); } forcedinline void handleEdgeTableLine (int x, int width, int alphaLevel) const throw() { DestPixelType* dest = linePixels + x; alphaLevel = (alphaLevel * extraAlpha) >> 8; + x -= xOffset; + + jassert (repeatPattern || (x >= 0 && x + width <= srcData.width)); if (alphaLevel < 0xfe) { do { - dest++ ->blend (sourceLineStart [x++], alphaLevel); - + dest++ ->blend (sourceLineStart [repeatPattern ? (x++ % srcData.width) : x++], alphaLevel); } while (--width > 0); } else { - copyRow (dest, sourceLineStart + x, width); + if (repeatPattern) + { + do + { + dest++ ->blend (sourceLineStart [x++ % srcData.width]); + } while (--width > 0); + } + else + { + copyRow (dest, sourceLineStart + x, width); + } } } + void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) throw() + { + jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width); + SrcPixelType* sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y - yOffset); + uint8* mask = (uint8*) (sourceLineStart + x - xOffset); + + if (sizeof (SrcPixelType) == sizeof (PixelARGB)) + mask += PixelARGB::indexA; + + et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width); + } + private: const Image::BitmapData& destData; const Image::BitmapData& srcData; - const int extraAlpha; + const int extraAlpha, xOffset, yOffset; DestPixelType* linePixels; SrcPixelType* sourceLineStart; @@ -81759,7 +81045,7 @@ private: const ImageFillEdgeTableRenderer& operator= (const ImageFillEdgeTableRenderer&); }; -template +template class TransformedImageFillEdgeTableRenderer { public: @@ -81822,6 +81108,18 @@ public: } } + void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) throw() + { + uint8* mask = (uint8*) alloca (sizeof (SrcPixelType) * width); + y = y_; + generate ((SrcPixelType*) mask, x, width); + + if (sizeof (SrcPixelType) == sizeof (PixelARGB)) + mask += PixelARGB::indexA; + + et.clipLineToMask (x, y_, mask, sizeof (SrcPixelType), width); + } + private: void generate (PixelARGB* dest, const int x, int numPixels) throw() @@ -81838,6 +81136,12 @@ private: int loResX = hiResX >> 8; int loResY = hiResY >> 8; + if (repeatPattern) + { + loResX = safeModulo (loResX, srcData.width); + loResY = safeModulo (loResY, srcData.height); + } + if (betterQuality && ((unsigned int) loResX) < (unsigned int) maxX && ((unsigned int) loResY) < (unsigned int) maxY) @@ -81881,11 +81185,14 @@ private: } else { - // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable - if (loResX < 0) loResX = 0; - if (loResY < 0) loResY = 0; - if (loResX > maxX) loResX = maxX; - if (loResY > maxY) loResY = maxY; + if (! repeatPattern) + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + } dest->set (*(const PixelARGB*) this->srcData.getPixelPointer (loResX, loResY)); } @@ -81908,6 +81215,12 @@ private: int loResX = hiResX >> 8; int loResY = hiResY >> 8; + if (repeatPattern) + { + loResX = safeModulo (loResX, srcData.width); + loResY = safeModulo (loResY, srcData.height); + } + if (betterQuality && ((unsigned int) loResX) < (unsigned int) maxX && ((unsigned int) loResY) < (unsigned int) maxY) @@ -81947,11 +81260,14 @@ private: } else { - // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable - if (loResX < 0) loResX = 0; - if (loResY < 0) loResY = 0; - if (loResX > maxX) loResX = maxX; - if (loResY > maxY) loResY = maxY; + if (! repeatPattern) + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + } dest->set (*(const PixelRGB*) this->srcData.getPixelPointer (loResX, loResY)); } @@ -81974,6 +81290,12 @@ private: int loResX = hiResX >> 8; int loResY = hiResY >> 8; + if (repeatPattern) + { + loResX = safeModulo (loResX, srcData.width); + loResY = safeModulo (loResY, srcData.height); + } + if (betterQuality && ((unsigned int) loResX) < (unsigned int) maxX && ((unsigned int) loResY) < (unsigned int) maxY) @@ -81981,8 +81303,8 @@ private: hiResX &= 255; hiResY &= 255; - uint32 c = 256 * 128; const uint8* src = this->srcData.getPixelPointer (loResX, loResY); + uint32 c = 256 * 128; c += src[0] * ((256 - hiResX) * (256 - hiResY)); c += src[1] * (hiResX * (256 - hiResY)); src += this->srcData.lineStride; @@ -81993,11 +81315,14 @@ private: } else { - // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable - if (loResX < 0) loResX = 0; - if (loResY < 0) loResY = 0; - if (loResX > maxX) loResX = maxX; - if (loResY > maxY) loResY = maxY; + if (! repeatPattern) + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + } *((uint8*) dest) = *(this->srcData.getPixelPointer (loResX, loResY)); } @@ -82098,46 +81423,35 @@ private: class LLGCSavedState { public: - LLGCSavedState (const Rectangle& clip_, - const int xOffset_, const int yOffset_, - const Font& font_, const Colour& colour_, ColourGradient* const gradient_, + LLGCSavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_, + const Font& font_, const Graphics::FillType& fillType_, const Graphics::ResamplingQuality interpolationQuality_) throw() : edgeTable (new EdgeTableHolder (EdgeTable (clip_))), - xOffset (xOffset_), - yOffset (yOffset_), - font (font_), - colour (colour_), - gradient (gradient_), + xOffset (xOffset_), yOffset (yOffset_), + font (font_), fillType (fillType_), interpolationQuality (interpolationQuality_) { } LLGCSavedState (const LLGCSavedState& other) throw() - : edgeTable (other.edgeTable), - xOffset (other.xOffset), - yOffset (other.yOffset), - font (other.font), - colour (other.colour), - gradient (other.gradient), - interpolationQuality (other.interpolationQuality) + : edgeTable (other.edgeTable), xOffset (other.xOffset), + yOffset (other.yOffset), font (other.font), + fillType (other.fillType), interpolationQuality (other.interpolationQuality) { - if (gradient != 0) - gradient = new ColourGradient (*gradient); } ~LLGCSavedState() throw() { - delete gradient; } - bool reduce (int x, int y, int w, int h) throw() + bool clipToRectangle (const Rectangle& r) throw() { dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.clipToRectangle (Rectangle (x, y, w, h)); + edgeTable->edgeTable.clipToRectangle (r); return ! edgeTable->edgeTable.isEmpty(); } - bool reduce (const RectangleList& r) throw() + bool clipToRectangleList (const RectangleList& r) throw() { dupeEdgeTableIfMultiplyReferenced(); RectangleList totalArea (edgeTable->edgeTable.getMaximumBounds()); @@ -82149,32 +81463,18 @@ public: return ! edgeTable->edgeTable.isEmpty(); } - bool exclude (int x, int y, int w, int h) throw() + bool excludeClipRectangle (const Rectangle& r) throw() { dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.excludeRectangle (Rectangle (x, y, w, h)); + edgeTable->edgeTable.excludeRectangle (r); return ! edgeTable->edgeTable.isEmpty(); } - bool reduce (const Path& p, const AffineTransform& transform) throw() + void clipToPath (const Path& p, const AffineTransform& transform) throw() { dupeEdgeTableIfMultiplyReferenced(); EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); edgeTable->edgeTable.clipToEdgeTable (et); - return ! edgeTable->edgeTable.isEmpty(); - } - - bool reduce (const Image& image, int x, int y) throw() - { - dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.clipToImageAlpha (image, x, y); - return ! edgeTable->edgeTable.isEmpty(); - } - - bool reduce (const Image& image, const AffineTransform& transform) throw() - { - jassertfalse - return true; } void fillEdgeTable (Image& image, EdgeTable& et, const bool replaceContents = false) throw() @@ -82183,13 +81483,13 @@ public: Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); - if (gradient != 0) + if (fillType.isGradient()) { jassert (! replaceContents); // that option is just for solid colours - ColourGradient g2 (*gradient); - + ColourGradient g2 (*(fillType.gradient)); const bool isIdentity = g2.transform.isOnlyTranslation(); + if (isIdentity) { // If our translation doesn't involve any distortion, we can speed it up.. @@ -82203,304 +81503,177 @@ public: } else { - g2.transform = g2.transform.translated ((float) xOffset, - (float) yOffset); + g2.transform = g2.transform.translated ((float) xOffset, (float) yOffset); } int numLookupEntries; PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); jassert (numLookupEntries > 0); - if (image.getFormat() == Image::RGB) + switch (image.getFormat()) { - jassert (destData.pixelStride == 3); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else if (image.getFormat() == Image::ARGB) - { - jassert (destData.pixelStride == 4); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (destData.pixelStride == 4); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } + case Image::ARGB: renderGradient (et, destData, g2, lookupTable, numLookupEntries, isIdentity); break; + case Image::RGB: renderGradient (et, destData, g2, lookupTable, numLookupEntries, isIdentity); break; + default: renderGradient (et, destData, g2, lookupTable, numLookupEntries, isIdentity); break; } juce_free (lookupTable); } + else if (fillType.isTiledImage()) + { + renderImage (image, *(fillType.image), Rectangle (0, 0, fillType.image->getWidth(), fillType.image->getHeight()), + AffineTransform::translation ((float) fillType.imageX, (float) fillType.imageY), &et); + } else { - const PixelARGB fillColour (colour.getPixelARGB()); + const PixelARGB fillColour (fillType.colour.getPixelARGB()); - if (replaceContents) + switch (image.getFormat()) { - if (image.getFormat() == Image::RGB) - { - jassert (destData.pixelStride == 3); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - else if (image.getFormat() == Image::ARGB) - { - jassert (destData.pixelStride == 4); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (destData.pixelStride == 1); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - } - else - { - if (image.getFormat() == Image::RGB) - { - jassert (destData.pixelStride == 3); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - else if (image.getFormat() == Image::ARGB) - { - jassert (destData.pixelStride == 4); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (destData.pixelStride == 1); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } + case Image::ARGB: renderSolidFill2 (et, destData, fillColour, replaceContents); break; + case Image::RGB: renderSolidFill2 (et, destData, fillColour, replaceContents); break; + default: renderSolidFill2 (et, destData, fillColour, replaceContents); break; } } } - void renderImage (Image& destImage, const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t) throw() + void renderImage (Image& destImage, const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& t, const EdgeTable* const tiledFillClipRegion) throw() { - if (t.isSingularity()) - return; - - const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + jassert (Rectangle (0, 0, sourceImage.getWidth(), sourceImage.getHeight()).contains (srcClip)); + + const Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); + const Image::BitmapData srcData (sourceImage, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); + const int alpha = fillType.colour.getAlpha(); + const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); + if (transform.isOnlyTranslation()) { // If our translation doesn't involve any distortion, just use a simple blit.. - const int tx = (int) (t.getTranslationX() * 256.0f); - const int ty = (int) (t.getTranslationY() * 256.0f); + const int tx = (int) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); if ((! betterQuality) || ((tx | ty) & 224) == 0) { - renderImage (destImage, sourceImage, (tx + 128) >> 8, (ty + 128) >> 8); + const Rectangle srcRect (srcClip.translated ((tx + 128) >> 8, (ty + 128) >> 8)); + + if (tiledFillClipRegion != 0) + { + blittedRenderImage3 (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, srcRect.getX(), srcRect.getY()); + } + else + { + EdgeTable et (srcRect.getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); + et.clipToEdgeTable (edgeTable->edgeTable); + + if (! et.isEmpty()) + blittedRenderImage3 (sourceImage, destImage, et, destData, srcData, alpha, srcRect.getX(), srcRect.getY()); + } + return; } } - Path p; - p.addRectangle ((float) srcClipX, (float) srcClipY, (float) srcClipW, (float) srcClipH); + if (transform.isSingularity()) + return; - EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); - et.clipToEdgeTable (edgeTable->edgeTable); - - Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); - Image::BitmapData srcData (sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - - const int extraAlpha = colour.getAlpha(); - - switch (sourceImage.getFormat()) + if (tiledFillClipRegion != 0) { - case Image::ARGB: - if (destImage.getFormat() == Image::ARGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else if (destImage.getFormat() == Image::RGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else - { - jassert (destImage.getFormat() == Image::SingleChannel) - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - break; + transformedRenderImage3 (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, transform, betterQuality); + } + else + { + Path p; + p.addRectangle (srcClip); - case Image::RGB: - if (destImage.getFormat() == Image::ARGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else if (destImage.getFormat() == Image::RGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else - { - jassert (destImage.getFormat() == Image::SingleChannel) - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - break; + EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); + et.clipToEdgeTable (edgeTable->edgeTable); - default: - jassert (sourceImage.getFormat() == Image::SingleChannel); - - if (destImage.getFormat() == Image::ARGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else if (destImage.getFormat() == Image::RGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else - { - jassert (destImage.getFormat() == Image::SingleChannel) - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - break; + if (! et.isEmpty()) + transformedRenderImage3 (sourceImage, destImage, et, destData, srcData, alpha, transform, betterQuality); } } - void renderImage (Image& destImage, const Image& sourceImage, int imageX, int imageY) throw() + void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& transform) throw() { - EdgeTable et (Rectangle (imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight()) - .getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); - et.clipToEdgeTable (edgeTable->edgeTable); - - if (et.isEmpty()) - return; - - Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); - Image::BitmapData srcData (sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - srcData.data = srcData.getPixelPointer (-imageX, -imageY); - - const int alpha = colour.getAlpha(); - - switch (destImage.getFormat()) + if (! image.hasAlphaChannel()) { - case Image::RGB: - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - break; - - case Image::ARGB: - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - break; - - default: - jassert (destImage.getFormat() == Image::SingleChannel); - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - break; + Path p; + p.addRectangle (srcClip); + clipToPath (p, transform); + return; } + + dupeEdgeTableIfMultiplyReferenced(); + + const Image::BitmapData srcData (image, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); + const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); + EdgeTable& et = edgeTable->edgeTable; + + if (transform.isOnlyTranslation()) + { + // If our translation doesn't involve any distortion, just use a simple blit.. + const int tx = (int) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); + + if ((! betterQuality) || ((tx | ty) & 224) == 0) + { + const int imageX = ((tx + 128) >> 8); + const int imageY = ((ty + 128) >> 8); + + if (image.getFormat() == Image::ARGB) + straightClipImage (et, srcData, imageX, imageY); + else + straightClipImage (et, srcData, imageX, imageY); + + return; + } + } + + if (transform.isSingularity()) + { + et.clipToRectangle (Rectangle()); + return; + } + + { + Path p; + p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); + EdgeTable et2 (et.getMaximumBounds(), p, transform); + et.clipToEdgeTable (et2); + } + + if (! et.isEmpty()) + { + if (image.getFormat() == Image::ARGB) + transformedClipImage (et, srcData, transform, betterQuality); + else + transformedClipImage (et, srcData, transform, betterQuality); + } + } + + template + void transformedClipImage (EdgeTable& et, const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality) throw() + { + TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); + + for (int y = 0; y < et.getMaximumBounds().getHeight(); ++y) + renderer.clipEdgeTableLine (et, et.getMaximumBounds().getX(), y + et.getMaximumBounds().getY(), + et.getMaximumBounds().getWidth()); + } + + template + void straightClipImage (EdgeTable& et, const Image::BitmapData& srcData, int imageX, int imageY) throw() + { + Rectangle r (imageX, imageY, srcData.width, srcData.height); + et.clipToRectangle (r); + + ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); + + for (int y = 0; y < r.getHeight(); ++y) + renderer.clipEdgeTableLine (et, r.getX(), y + r.getY(), r.getWidth()); } class EdgeTableHolder : public ReferenceCountedObject @@ -82516,8 +81689,7 @@ public: ReferenceCountedObjectPtr edgeTable; int xOffset, yOffset; Font font; - Colour colour; - ColourGradient* gradient; + Graphics::FillType fillType; Graphics::ResamplingQuality interpolationQuality; private: @@ -82528,6 +81700,113 @@ private: if (edgeTable->getReferenceCount() > 1) edgeTable = new EdgeTableHolder (edgeTable->edgeTable); } + + template + void renderGradient (EdgeTable& et, const Image::BitmapData& destData, const ColourGradient& g, + const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity) throw() + { + jassert (destData.pixelStride == sizeof (DestPixelType)); + + if (g.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g, lookupTable, numLookupEntries); + et.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + + template + void renderSolidFill1 (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour) throw() + { + jassert (destData.pixelStride == sizeof (DestPixelType)); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + + template + void renderSolidFill2 (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents) throw() + { + if (replaceContents) + renderSolidFill1 (et, destData, fillColour); + else + renderSolidFill1 (et, destData, fillColour); + } + + template + void transformedRenderImage1 (const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, const AffineTransform& transform, const bool betterQuality) throw() + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, alpha, betterQuality); + et.iterate (renderer); + } + + template + void transformedRenderImage2 (Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, const AffineTransform& transform, const bool betterQuality) throw() + { + switch (destImage.getFormat()) + { + case Image::ARGB: transformedRenderImage1 (et, destData, srcData, alpha, transform, betterQuality); break; + case Image::RGB: transformedRenderImage1 (et, destData, srcData, alpha, transform, betterQuality); break; + default: transformedRenderImage1 (et, destData, srcData, alpha, transform, betterQuality); break; + } + } + + template + void transformedRenderImage3 (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, const AffineTransform& transform, const bool betterQuality) throw() + { + switch (srcImage.getFormat()) + { + case Image::ARGB: transformedRenderImage2 (destImage, et, destData, srcData, alpha, transform, betterQuality); break; + case Image::RGB: transformedRenderImage2 (destImage, et, destData, srcData, alpha, transform, betterQuality); break; + default: transformedRenderImage2 (destImage, et, destData, srcData, alpha, transform, betterQuality); break; + } + } + + template + void blittedRenderImage1 (const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, int x, int y) throw() + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha, x, y); + et.iterate (renderer); + } + + template + void blittedRenderImage2 (Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, + const Image::BitmapData& srcData, const int alpha, int x, int y) throw() + { + switch (destImage.getFormat()) + { + case Image::ARGB: blittedRenderImage1 (et, destData, srcData, alpha, x, y); break; + case Image::RGB: blittedRenderImage1 (et, destData, srcData, alpha, x, y); break; + default: blittedRenderImage1 (et, destData, srcData, alpha, x, y); break; + } + } + + template + void blittedRenderImage3 (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, + const Image::BitmapData& srcData, const int alpha, int x, int y) throw() + { + switch (srcImage.getFormat()) + { + case Image::ARGB: blittedRenderImage2 (destImage, et, destData, srcData, alpha, x, y); break; + case Image::RGB: blittedRenderImage2 (destImage, et, destData, srcData, alpha, x, y); break; + default: blittedRenderImage2 (destImage, et, destData, srcData, alpha, x, y); break; + } + } }; LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image_) @@ -82535,7 +81814,7 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image stateStack (20) { currentState = new LLGCSavedState (Rectangle (0, 0, image_.getWidth(), image_.getHeight()), - 0, 0, Font(), Colours::black, 0, Graphics::mediumResamplingQuality); + 0, 0, Font(), Graphics::FillType(), Graphics::mediumResamplingQuality); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() @@ -82554,36 +81833,38 @@ void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y) currentState->yOffset += y; } -bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (int x, int y, int w, int h) +bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle& r) { - return currentState->reduce (x + currentState->xOffset, y + currentState->yOffset, w, h); + return currentState->clipToRectangle (r.translated (currentState->xOffset, currentState->yOffset)); } -bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (const RectangleList& clipRegion) +bool LowLevelGraphicsSoftwareRenderer::clipToRectangleList (const RectangleList& clipRegion) { RectangleList temp (clipRegion); temp.offsetAll (currentState->xOffset, currentState->yOffset); - return currentState->reduce (temp); + return currentState->clipToRectangleList (temp); } -void LowLevelGraphicsSoftwareRenderer::excludeClipRegion (int x, int y, int w, int h) +void LowLevelGraphicsSoftwareRenderer::excludeClipRectangle (const Rectangle& r) { - currentState->exclude (x + currentState->xOffset, y + currentState->yOffset, w, h); + currentState->excludeClipRectangle (r.translated (currentState->xOffset, currentState->yOffset)); } void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform) { + currentState->clipToPath (path, transform.translated ((float) currentState->xOffset, (float) currentState->yOffset)); } -void LowLevelGraphicsSoftwareRenderer::clipToImage (Image& image, int imageX, int imageY) +void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) { + currentState->clipToImageAlpha (sourceImage, srcClip, transform.translated ((float) currentState->xOffset, (float) currentState->yOffset)); } -bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (int x, int y, int w, int h) +bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle& r) { return currentState->edgeTable->edgeTable.getMaximumBounds() - .intersects (Rectangle (x + currentState->xOffset, y + currentState->yOffset, w, h)); + .intersects (r.translated (currentState->xOffset, currentState->yOffset)); } const Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const @@ -82617,21 +81898,24 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() } } -void LowLevelGraphicsSoftwareRenderer::setColour (const Colour& colour_) +void LowLevelGraphicsSoftwareRenderer::setColour (const Colour& colour) { - deleteAndZero (currentState->gradient); - currentState->colour = colour_; + currentState->fillType.setColour (colour); } -void LowLevelGraphicsSoftwareRenderer::setGradient (const ColourGradient& gradient_) +void LowLevelGraphicsSoftwareRenderer::setGradient (const ColourGradient& gradient) { - delete currentState->gradient; - currentState->gradient = new ColourGradient (gradient_); + currentState->fillType.setGradient (gradient); +} + +void LowLevelGraphicsSoftwareRenderer::setTiledFill (const Image& image, int x, int y) +{ + currentState->fillType.setTiledImage (image, x, y); } void LowLevelGraphicsSoftwareRenderer::setOpacity (float opacity) { - currentState->colour = currentState->colour.withAlpha (opacity); + currentState->fillType.colour = currentState->fillType.colour.withAlpha (opacity); } void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) @@ -82639,14 +81923,14 @@ void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::Resamp currentState->interpolationQuality = quality; } -void LowLevelGraphicsSoftwareRenderer::fillRect (int x, int y, int w, int h, const bool replaceExistingContents) +void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle& r, const bool replaceExistingContents) { - x += currentState->xOffset; - y += currentState->yOffset; + const Rectangle& totalClip = currentState->edgeTable->edgeTable.getMaximumBounds(); + const Rectangle clipped (totalClip.getIntersection (r.translated (currentState->xOffset, currentState->yOffset))); - if (Rectangle::intersectRectangles (x, y, w, h, 0, 0, image.getWidth(), image.getHeight())) + if (! clipped.isEmpty()) { - EdgeTable et (Rectangle (x, y, w, h)); + EdgeTable et (clipped); currentState->fillEdgeTable (image, et, replaceExistingContents); } } @@ -82660,60 +81944,11 @@ void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineT currentState->fillEdgeTable (image, et); } -void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& sourceImage, int imageX, int imageY) +void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { - imageX += currentState->xOffset; - imageY += currentState->yOffset; - - saveState(); - currentState->reduce (path, transform.translated ((float) currentState->xOffset, (float) currentState->yOffset)); - currentState->renderImage (image, sourceImage, imageX, imageY); - restoreState(); -} - -void LowLevelGraphicsSoftwareRenderer::fillAlphaChannel (const Image& clipImage, int x, int y) -{ - x += currentState->xOffset; - y += currentState->yOffset; - - Rectangle maxBounds (currentState->edgeTable->edgeTable.getMaximumBounds()); - EdgeTable et (maxBounds.getIntersection (Rectangle (x, y, clipImage.getWidth(), clipImage.getHeight()))); - et.clipToImageAlpha (clipImage, x, y); - - currentState->fillEdgeTable (image, et); -} - -void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY) -{ - alphaImageX += currentState->xOffset; - alphaImageY += currentState->yOffset; - fillerImageX += currentState->xOffset; - fillerImageY += currentState->yOffset; - - saveState(); - currentState->reduce (alphaImage, alphaImageX, alphaImageY); - currentState->renderImage (image, fillerImage, fillerImageX, fillerImageY); - restoreState(); -} - -void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) -{ - dx += currentState->xOffset; - dy += currentState->yOffset; - - saveState(); - currentState->reduce (dx, dy, dw, dh); - currentState->renderImage (image, sourceImage, dx - sx, dy - sy); - restoreState(); -} - -void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t) -{ - currentState->renderImage (image, sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, t); + currentState->renderImage (image, sourceImage, srcClip, transform, + fillEntireClipAsTiles ? &(currentState->edgeTable->edgeTable) : 0); } void LowLevelGraphicsSoftwareRenderer::drawLine (double x1, double y1, double x2, double y2) @@ -82736,19 +81971,170 @@ void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double l currentState->fillEdgeTable (image, et); } +class GlyphCache : private DeletedAtShutdown +{ +public: + GlyphCache() throw() + : accessCounter (0), hits (0), misses (0) + { + enlargeCache (120); + } + + ~GlyphCache() throw() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); + + void drawGlyph (LLGCSavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) throw() + { + ++accessCounter; + int oldestCounter = INT_MAX; + CachedGlyph* oldest = 0; + + for (int i = glyphs.size(); --i >= 0;) + { + CachedGlyph* const glyph = glyphs.getUnchecked (i); + + if (glyph->glyph == glyphNumber && glyph->font == font) + { + ++hits; + glyph->lastAccessCount = accessCounter; + glyph->draw (state, image, x, y); + return; + } + + if (glyph->lastAccessCount <= oldestCounter) + { + oldestCounter = glyph->lastAccessCount; + oldest = glyph; + } + } + + ++misses; + + if (hits + misses > (glyphs.size() << 4)) + { + if (misses * 2 > hits) + enlargeCache (glyphs.size() + 32); + + hits = 0; + misses = 0; + oldest = glyphs.getLast(); + } + + jassert (oldest != 0); + oldest->lastAccessCount = accessCounter; + oldest->generate (font, glyphNumber); + oldest->draw (state, image, x, y); + } + + class CachedGlyph + { + public: + CachedGlyph() throw() + : glyph (0), lastAccessCount (0) + { + edgeTable = 0; + } + + ~CachedGlyph() throw() + { + delete edgeTable; + } + + void draw (LLGCSavedState& state, Image& image, const float x, const float y) const throw() + { + if (edgeTable != 0) + { + EdgeTable et (*edgeTable); + et.translate (x, roundFloatToInt (y)); + state.fillEdgeTable (image, et, false); + } + } + + void generate (const Font& newFont, const int glyphNumber) throw() + { + font = newFont; + glyph = glyphNumber; + deleteAndZero (edgeTable); + + Path glyphPath; + font.getTypeface()->getOutlineForGlyph (glyphNumber, glyphPath); + + if (! glyphPath.isEmpty()) + { + const float fontHeight = font.getHeight(); + const AffineTransform transform (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)); + + float px, py, pw, ph; + glyphPath.getBoundsTransformed (transform, px, py, pw, ph); + + Rectangle clip ((int) floorf (px), (int) floorf (py), + roundFloatToInt (pw) + 2, roundFloatToInt (ph) + 2); + + edgeTable = new EdgeTable (clip, glyphPath, transform); + } + } + + int glyph, lastAccessCount; + Font font; + + juce_UseDebuggingNewOperator + + private: + EdgeTable* edgeTable; + + CachedGlyph (const CachedGlyph&); + const CachedGlyph& operator= (const CachedGlyph&); + }; + + juce_UseDebuggingNewOperator + +private: + void enlargeCache (const int num) throw() + { + while (glyphs.size() < num) + glyphs.add (new CachedGlyph()); + } + + OwnedArray glyphs; + int accessCounter, hits, misses; + + GlyphCache (const GlyphCache&); + const GlyphCache& operator= (const GlyphCache&); +}; + +juce_ImplementSingleton_SingleThreaded (GlyphCache); + void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) { currentState->font = newFont; } -void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, float x, float y) +const Font LowLevelGraphicsSoftwareRenderer::getFont() { - currentState->font.renderGlyphIndirectly (*this, glyphNumber, x, y); + return currentState->font; } void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) { - currentState->font.renderGlyphIndirectly (*this, glyphNumber, transform); + Font& f = currentState->font; + + if (transform.isOnlyTranslation()) + { + GlyphCache::getInstance()->drawGlyph (*currentState, image, f, glyphNumber, + transform.getTranslationX() + (float) currentState->xOffset, + roundFloatToInt (transform.getTranslationY() + (float) currentState->yOffset)); + } + else + { + Path p; + f.getTypeface()->getOutlineForGlyph (glyphNumber, p); + + fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()).followedBy (transform)); + } } #if JUCE_MSVC @@ -85854,183 +85240,6 @@ Typeface* Font::getTypeface() const throw() return font->typeface; } -class FontGlyphAlphaMap -{ -public: - - FontGlyphAlphaMap() throw() - : glyph (0), lastAccessCount (0) - { - bitmap[0] = bitmap[1] = 0; - } - - ~FontGlyphAlphaMap() throw() - { - delete bitmap[0]; - delete bitmap[1]; - } - - void draw (LowLevelGraphicsContext& g, float x, const float y) const throw() - { - if (bitmap[0] != 0) - { - const float xFloor = floorf (x); - const int bitmapToUse = ((x - xFloor) >= 0.5f && bitmap[1] != 0) ? 1 : 0; - - g.fillAlphaChannel (*bitmap [bitmapToUse], - xOrigin [bitmapToUse] + (int) xFloor, - yOrigin [bitmapToUse] + roundFloatToInt(y)); - } - } - - void generate (const Font& font_, const int glyph_) throw() - { - font = font_; - glyph = glyph_; - - deleteAndZero (bitmap[0]); - deleteAndZero (bitmap[1]); - - Path glyphPath; - font.getTypeface()->getOutlineForGlyph (glyph_, glyphPath); - - if (! glyphPath.isEmpty()) - { - const float fontHeight = font.getHeight(); - const float fontHScale = fontHeight * font.getHorizontalScale(); - AffineTransform transform (AffineTransform::scale (fontHScale, fontHeight)); - Rectangle clip (-2048, -2048, 4096, 4096), pos; - - bitmap[0] = glyphPath.createMaskBitmap (transform, clip, pos); - xOrigin[0] = pos.getX(); - yOrigin[0] = pos.getY(); - - if (fontHScale < 30.0f) - { - bitmap[1] = glyphPath.createMaskBitmap (transform.translated (0.5f, 0.0f), clip, pos); - xOrigin[1] = pos.getX(); - yOrigin[1] = pos.getY(); - } - } - } - - int glyph, lastAccessCount; - Font font; - - juce_UseDebuggingNewOperator - -private: - Image* bitmap[2]; - int xOrigin[2], yOrigin[2]; - - FontGlyphAlphaMap (const FontGlyphAlphaMap&); - const FontGlyphAlphaMap& operator= (const FontGlyphAlphaMap&); -}; - -class GlyphCache : private DeletedAtShutdown -{ -public: - GlyphCache() throw() - : accessCounter (0) - { - setCacheSize (120); - } - - ~GlyphCache() throw() - { - clearSingletonInstance(); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); - - void drawGlyph (LowLevelGraphicsContext& g, const Font& font, int glyphNumber, float x, float y) throw() - { - ++accessCounter; - - int oldestCounter = INT_MAX; - FontGlyphAlphaMap* oldest = 0; - - for (int i = glyphs.size(); --i >= 0;) - { - FontGlyphAlphaMap* const glyph = glyphs.getUnchecked (i); - - if (glyph->glyph == glyphNumber - && glyph->font == font) - { - ++hits; - glyph->lastAccessCount = accessCounter; - glyph->draw (g, x, y); - return; - } - - if (glyph->lastAccessCount <= oldestCounter) - { - oldestCounter = glyph->lastAccessCount; - oldest = glyph; - } - } - - ++misses; - - if (hits + misses > (glyphs.size() << 4)) - { - if (misses * 2 > hits) - setCacheSize (glyphs.size() + 32); - - hits = 0; - misses = 0; - oldest = glyphs.getUnchecked (0); - } - - jassert (oldest != 0); - oldest->lastAccessCount = accessCounter; - oldest->generate (font, glyphNumber); - oldest->draw (g, x, y); - } - - void setCacheSize (int num) throw() - { - if (glyphs.size() != num) - { - glyphs.clear(); - - while (--num >= 0) - glyphs.add (new FontGlyphAlphaMap()); - - hits = 0; - misses = 0; - } - } - - juce_UseDebuggingNewOperator - -private: - OwnedArray glyphs; - int accessCounter, hits, misses; - - GlyphCache (const GlyphCache&); - const GlyphCache& operator= (const GlyphCache&); -}; - -juce_ImplementSingleton_SingleThreaded (GlyphCache); - -void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, float x, float y) -{ - if (font->height < 80.0f) - GlyphCache::getInstance()->drawGlyph (g, *this, glyphNumber, x, y); - else - renderGlyphIndirectly (g, glyphNumber, AffineTransform::translation (x, y)); -} - -void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, const AffineTransform& transform) -{ - Path p; - getTypeface()->getOutlineForGlyph (glyphNumber, p); - - g.fillPath (p, AffineTransform::scale (font->height * font->horizontalScale, font->height) - .followedBy (transform)); -} - END_JUCE_NAMESPACE /********* End of inlined file: juce_Font.cpp *********/ @@ -86049,7 +85258,7 @@ void PositionedGlyph::draw (const Graphics& g) const throw() if (! isWhitespace()) { g.getInternalContext()->setFont (font); - g.getInternalContext()->drawGlyph (glyph, x, y); + g.getInternalContext()->drawGlyph (glyph, AffineTransform::translation (x, y)); } } @@ -88516,11 +87725,40 @@ const Point Path::getCurrentPosition() const void Path::addRectangle (const float x, const float y, const float w, const float h) throw() { - startNewSubPath (x, y + h); - lineTo (x, y); - lineTo (x + w, y); - lineTo (x + w, y + h); - closeSubPath(); + float x1 = x, y1 = y, x2 = x + w, y2 = y + h; + + if (w < 0) + swapVariables (x1, x2); + + if (h < 0) + swapVariables (y1, y2); + + ensureAllocatedSize (numElements + 13); + + elements [numElements++] = moveMarker; + elements [numElements++] = x1; + elements [numElements++] = y2; + elements [numElements++] = lineMarker; + elements [numElements++] = x1; + elements [numElements++] = y1; + elements [numElements++] = lineMarker; + elements [numElements++] = x2; + elements [numElements++] = y1; + elements [numElements++] = lineMarker; + elements [numElements++] = x2; + elements [numElements++] = y2; + elements [numElements++] = closeSubPathMarker; + + pathXMin = jmin (pathXMin, x1); + pathXMax = jmax (pathXMax, x2); + pathYMin = jmin (pathYMin, y1); + pathYMax = jmax (pathYMax, y2); +} + +void Path::addRectangle (const Rectangle& rectangle) throw() +{ + addRectangle ((float) rectangle.getX(), (float) rectangle.getY(), + (float) rectangle.getWidth(), (float) rectangle.getHeight()); } void Path::addRoundedRectangle (const float x, const float y, @@ -89733,70 +88971,6 @@ bool Path::Iterator::next() return false; } -class MaskBitmapRenderer -{ -public: - MaskBitmapRenderer (const Image::BitmapData& destData_) throw() - : destData (destData_) - { - } - - forcedinline void setEdgeTableYPos (const int y) throw() - { - lineStart = destData.getLinePointer (y); - } - - forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() - { - lineStart [x] = (uint8) alphaLevel; - } - - forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() - { - uint8* d = lineStart + x; - - while (--width >= 0) - *d++ = (uint8) alphaLevel; - } - -private: - const Image::BitmapData& destData; - uint8* lineStart; - - MaskBitmapRenderer (const MaskBitmapRenderer&); - const MaskBitmapRenderer& operator= (const MaskBitmapRenderer&); -}; - -Image* Path::createMaskBitmap (const AffineTransform& transform, - const Rectangle& clipRegion, - Rectangle& imagePosition) const throw() -{ - if (isEmpty()) - return 0; - - float px, py, pw, ph; - getBoundsTransformed (transform, px, py, pw, ph); - - imagePosition = clipRegion.getIntersection (Rectangle ((int) floorf (px), (int) floorf (py), - roundFloatToInt (pw) + 2, roundFloatToInt (ph) + 2)); - - if (imagePosition.isEmpty()) - return 0; - - Image* im = Image::createNativeImage (Image::SingleChannel, imagePosition.getWidth(), imagePosition.getHeight(), true); - - EdgeTable edgeTable (Rectangle (0, 0, imagePosition.getWidth(), imagePosition.getHeight()), - *this, transform.translated ((float) -imagePosition.getX(), (float) -imagePosition.getY())); - - const Image::BitmapData destData (*im, 0, 0, imagePosition.getWidth(), imagePosition.getHeight(), true); - - jassert (destData.pixelStride == 1); - MaskBitmapRenderer renderer (destData); - edgeTable.iterate (renderer); - - return im; -} - END_JUCE_NAMESPACE /********* End of inlined file: juce_Path.cpp *********/ @@ -238050,7 +237224,7 @@ private: updateCurrentModifiers(); LowLevelGraphicsSoftwareRenderer context (*offscreenImage); - context.reduceClipRegion (contextClip); + context.clipToRectangleList (contextClip); context.setOrigin (-x, -y); handlePaint (context); @@ -262275,7 +261449,9 @@ class CoreGraphicsContext : public LowLevelGraphicsContext public: CoreGraphicsContext (CGContextRef context_, const float flipHeight_) : context (context_), - flipHeight (flipHeight_) + flipHeight (flipHeight_), + gradientLookupTable (0), + numGradientLookupEntries (0) { CGContextRetain (context); CGContextSetShouldSmoothFonts (context, true); @@ -262295,6 +261471,7 @@ public: CGColorSpaceRelease (rgbColourSpace); CGColorSpaceRelease (greyColourSpace); delete state; + delete gradientLookupTable; } bool isVectorDevice() const { return false; } @@ -262304,13 +261481,13 @@ public: CGContextTranslateCTM (context, x, -y); } - bool reduceClipRegion (int x, int y, int w, int h) + bool clipToRectangle (const Rectangle& r) { - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); return ! isClipEmpty(); } - bool reduceClipRegion (const RectangleList& clipRegion) + bool clipToRectangleList (const RectangleList& clipRegion) { const int numRects = clipRegion.getNumRectangles(); CGRect* const rects = new CGRect [numRects]; @@ -262326,11 +261503,59 @@ public: return ! isClipEmpty(); } - void excludeClipRegion (int x, int y, int w, int h) + void excludeClipRectangle (const Rectangle& r) { - RectangleList r (getClipBounds()); - r.subtract (Rectangle (x, y, w, h)); - reduceClipRegion (r); + RectangleList remaining (getClipBounds()); + remaining.subtract (r); + clipToRectangleList (remaining); + } + + void clipToPath (const Path& path, const AffineTransform& transform) + { + createPath (path, transform); + CGContextClip (context); + } + + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) + { + if (! transform.isSingularity()) + { + Image* singleChannelImage = createAlphaChannelImage (sourceImage); + CGImageRef image = createImage (*singleChannelImage, true); + + flip(); + AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + applyTransform (t); + + CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + CGContextClipToMask (context, r, image); + + applyTransform (t.inverted()); + flip(); + + CGImageRelease (image); + deleteAlphaChannelImage (sourceImage, singleChannelImage); + } + } + + bool clipRegionIntersects (const Rectangle& r) + { + return getClipBounds().intersects (r); + } + + const Rectangle getClipBounds() const + { + CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); + + return Rectangle (roundFloatToInt (bounds.origin.x), + roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)), + roundFloatToInt (bounds.size.width), + roundFloatToInt (bounds.size.height)); + } + + bool isClipEmpty() const + { + return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); } void saveState() @@ -262357,30 +261582,9 @@ public: } } - bool clipRegionIntersects (int x, int y, int w, int h) - { - return getClipBounds().intersects (Rectangle (x, y, w, h)); - } - - const Rectangle getClipBounds() const - { - CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); - - return Rectangle (roundFloatToInt (bounds.origin.x), - roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)), - roundFloatToInt (bounds.size.width), - roundFloatToInt (bounds.size.height)); - } - - bool isClipEmpty() const - { - return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); - } - void setColour (const Colour& colour) { - state->colour = colour; - deleteAndZero (state->gradient); + state->fillType.setColour (colour); CGContextSetRGBFillColor (context, colour.getFloatRed(), colour.getFloatGreen(), @@ -262390,15 +261594,17 @@ public: void setGradient (const ColourGradient& gradient) { - if (state->gradient == 0) - state->gradient = new ColourGradient (gradient); - else - *state->gradient = gradient; + state->fillType.setGradient (gradient); + } + + void setTiledFill (const Image& image, int x, int y) + { + state->fillType.setTiledImage (image, x, y); } void setOpacity (float opacity) { - setColour (state->colour.withAlpha (opacity)); + state->fillType.colour = state->fillType.colour.withAlpha (opacity); } void setInterpolationQuality (Graphics::ResamplingQuality quality) @@ -262408,36 +261614,46 @@ public: : kCGInterpolationHigh); } - void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) + void fillRect (const Rectangle& r, const bool replaceExistingContents) { + CGRect cgRect = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()); + if (replaceExistingContents) { #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 - CGContextClearRect (context, CGRectMake (x, y, w, h)); + CGContextClearRect (context, cgRect); #else #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 if (CGContextDrawLinearGradient == 0) // (just a way of checking whether we're running in 10.5 or later) - CGContextClearRect (context, CGRectMake (x, y, w, h)); + CGContextClearRect (context, cgRect); else #endif CGContextSetBlendMode (context, kCGBlendModeCopy); #endif - fillRect (x, y, w, h, false); + fillRect (r, false); CGContextSetBlendMode (context, kCGBlendModeNormal); } else { - if (state->gradient == 0) + if (state->fillType.isColour()) { - CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextFillRect (context, cgRect); + } + else if (state->fillType.isGradient()) + { + CGContextSaveGState (context); + CGContextClipToRect (context, cgRect); + flip(); + drawGradient(); + CGContextRestoreGState (context); } else { CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); - flip(); - drawGradient(); + CGContextClipToRect (context, cgRect); + drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), + AffineTransform::translation (state->fillType.imageX, state->fillType.imageY), true); CGContextRestoreGState (context); } } @@ -262447,7 +261663,7 @@ public: { CGContextSaveGState (context); - if (state->gradient == 0) + if (state->fillType.isColour()) { flip(); applyTransform (transform); @@ -262458,101 +261674,77 @@ public: else CGContextEOFillPath (context); } - else + else if (state->fillType.isGradient()) { createPath (path, transform); CGContextClip (context); flip(); - applyTransform (state->gradient->transform); + applyTransform (state->fillType.gradient->transform); drawGradient(); } + else + { + createPath (path, transform); + CGContextClip (context); + drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), + AffineTransform::translation (state->fillType.imageX, state->fillType.imageY), true); + } CGContextRestoreGState (context); } - void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY) - { - CGContextSaveGState (context); - createPath (path, transform); - CGContextClip (context); - blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0); - CGContextRestoreGState (context); - } - - void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) - { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); - - CGContextSaveGState (context); - CGContextSetAlpha (context, 1.0f); - - CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), - alphaImage.getWidth(), alphaImage.getHeight()); - CGContextClipToMask (context, r, image); - - fillRect (alphaImageX, alphaImageY, alphaImage.getWidth(), alphaImage.getHeight(), false); - - CGContextRestoreGState (context); - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } - - void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY) - { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); - - CGContextSaveGState (context); - CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), - alphaImage.getWidth(), alphaImage.getHeight()); - CGContextClipToMask (context, r, image); - - blendImage (fillerImage, fillerImageX, fillerImageY, - fillerImage.getWidth(), fillerImage.getHeight(), - 0, 0); - - CGContextRestoreGState (context); - - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } - - void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY) - { - CGImageRef image = createImage (sourceImage, false); - - CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (destX, flipHeight - (destY + destH), destW, destH)); - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (destX - sourceX, - flipHeight - ((destY - sourceY) + sourceImage.getHeight()), - sourceImage.getWidth(), - sourceImage.getHeight()), image); - - CGContextRestoreGState (context); - CGImageRelease (image); - } - - void blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) + void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { CGImageRef fullImage = createImage (sourceImage, false); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), - srcClipW, srcClipH)); + CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); CGImageRelease (fullImage); CGContextSaveGState (context); + CGContextSetAlpha (context, state->fillType.colour.getFloatAlpha()); + flip(); applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + CGRect imageRect = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), - sourceImage.getHeight()), image); + if (fillEntireClipAsTiles) + { +#if JUCE_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + CGContextDrawTiledImage (context, imageRect, image); +#else + #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (CGContextDrawTiledImage != 0) + CGContextDrawTiledImage (context, imageRect, image); + else + #endif + { + // Fallback to manually doing a tiled fill on 10.4 + CGRect clip = CGRectIntegral (CGContextGetClipBoundingBox (context)); + const int iw = sourceImage.getWidth(); + const int ih = sourceImage.getHeight(); + + int x = 0, y = 0; + while (x > clip.origin.x) x -= iw; + while (y > clip.origin.y) y -= ih; + + const int right = clip.origin.x + clip.size.width; + const int bottom = clip.origin.y + clip.size.height; + + while (y < bottom) + { + for (int x2 = x; x2 < right; x2 += iw) + CGContextDrawImage (context, CGRectMake (x2, y, iw, ih), image); + + y += ih; + } + } +#endif + } + else + { + CGContextDrawImage (context, imageRect, image); + } CGImageRelease (image); CGContextRestoreGState (context); @@ -262563,8 +261755,8 @@ public: CGContextSetLineCap (context, kCGLineCapSquare); CGContextSetLineWidth (context, 1.0f); CGContextSetRGBStrokeColor (context, - state->colour.getFloatRed(), state->colour.getFloatGreen(), - state->colour.getFloatBlue(), state->colour.getFloatAlpha()); + state->fillType.colour.getFloatRed(), state->fillType.colour.getFloatGreen(), + state->fillType.colour.getFloatBlue(), state->fillType.colour.getFloatAlpha()); CGPoint line[] = { { x1 + 0.5f, flipHeight - (y1 + 0.5f) }, { x2 + 0.5f, flipHeight - (y2 + 0.5f) } }; @@ -262604,40 +261796,46 @@ public: } } - void drawGlyph (int glyphNumber, float x, float y) + const Font getFont() { - if (state->fontRef != 0 && state->gradient == 0) - { - CGGlyph g = glyphNumber; - CGContextShowGlyphsAtPoint (context, x, flipHeight - roundFloatToInt (y), &g, 1); - } - else - { - state->font.renderGlyphIndirectly (*this, glyphNumber, x, y); - } + return state->font; } void drawGlyph (int glyphNumber, const AffineTransform& transform) { - if (state->fontRef != 0) + if (state->fontRef != 0 && state->fillType.isColour()) { - CGContextSaveGState (context); - flip(); - applyTransform (transform); + if (transform.isOnlyTranslation()) + { + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, transform.getTranslationX(), + flipHeight - roundFloatToInt (transform.getTranslationY()), &g, 1); + } + else + { + CGContextSaveGState (context); + flip(); + applyTransform (transform); - CGAffineTransform t = state->fontTransform; - t.d = -t.d; - CGContextSetTextMatrix (context, t); + CGAffineTransform t = state->fontTransform; + t.d = -t.d; + CGContextSetTextMatrix (context, t); - CGGlyph g = glyphNumber; - CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); - CGContextSetTextMatrix (context, state->fontTransform); - CGContextRestoreGState (context); + CGContextSetTextMatrix (context, state->fontTransform); + CGContextRestoreGState (context); + } } else { - state->font.renderGlyphIndirectly (*this, glyphNumber, transform); + Path p; + Font& f = state->font; + f.getTypeface()->getOutlineForGlyph (glyphNumber, p); + + fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()) + .followedBy (transform)); } } @@ -262650,26 +261848,21 @@ private: struct SavedState { SavedState() throw() - : gradient (0), font (1.0f), fontRef (0), - fontTransform (CGAffineTransformIdentity) + : font (1.0f), fontRef (0), fontTransform (CGAffineTransformIdentity) { } SavedState (const SavedState& other) throw() - : colour (other.colour), - gradient (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0), - font (other.font), fontRef (other.fontRef), + : fillType (other.fillType), font (other.font), fontRef (other.fontRef), fontTransform (other.fontTransform) { } ~SavedState() throw() { - delete gradient; } - Colour colour; - ColourGradient* gradient; + Graphics::FillType fillType; Font font; CGFontRef fontRef; CGAffineTransform fontTransform; @@ -262677,21 +261870,31 @@ private: SavedState* state; OwnedArray stateStack; + PixelARGB* gradientLookupTable; + int numGradientLookupEntries; static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) { - const ColourGradient* const g = (const ColourGradient*) info; - const Colour c (g->getColourAtPosition (inData[0])); - outData[0] = c.getFloatRed(); - outData[1] = c.getFloatGreen(); - outData[2] = c.getFloatBlue(); - outData[3] = c.getFloatAlpha(); + const CoreGraphicsContext* const g = (const CoreGraphicsContext*) info; + + const int index = roundFloatToInt (g->numGradientLookupEntries * inData[0]); + PixelARGB colour (g->gradientLookupTable [jlimit (0, g->numGradientLookupEntries, index)]); + colour.unpremultiply(); + + outData[0] = colour.getRed() / 255.0f; + outData[1] = colour.getGreen() / 255.0f; + outData[2] = colour.getBlue() / 255.0f; + outData[3] = colour.getAlpha() / 255.0f; } - CGShadingRef createGradient (const ColourGradient* const gradient) const throw() + CGShadingRef createGradient (const ColourGradient* const gradient) throw() { + delete gradientLookupTable; + gradientLookupTable = gradient->createLookupTable (numGradientLookupEntries); + --numGradientLookupEntries; + CGShadingRef result = 0; - CGFunctionRef function = CGFunctionCreate ((void*) gradient, 1, 0, 4, 0, &gradientCallbacks); + CGFunctionRef function = CGFunctionCreate ((void*) this, 1, 0, 4, 0, &gradientCallbacks); CGPoint p1 (CGPointMake (gradient->x1, gradient->y1)); if (gradient->isRadial) @@ -262711,12 +261914,12 @@ private: return result; } - void drawGradient() const throw() + void drawGradient() throw() { CGContextSetAlpha (context, 1.0f); CGContextSetInterpolationQuality (context, kCGInterpolationDefault); // (This is required for 10.4, where there's a crash if // you draw a gradient with high quality interp enabled). - CGShadingRef shading = createGradient (state->gradient); + CGShadingRef shading = createGradient (state->fillType.gradient); CGContextDrawShading (context, shading); CGShadingRelease (shading); } @@ -266701,7 +265904,9 @@ class CoreGraphicsContext : public LowLevelGraphicsContext public: CoreGraphicsContext (CGContextRef context_, const float flipHeight_) : context (context_), - flipHeight (flipHeight_) + flipHeight (flipHeight_), + gradientLookupTable (0), + numGradientLookupEntries (0) { CGContextRetain (context); CGContextSetShouldSmoothFonts (context, true); @@ -266721,6 +265926,7 @@ public: CGColorSpaceRelease (rgbColourSpace); CGColorSpaceRelease (greyColourSpace); delete state; + delete gradientLookupTable; } bool isVectorDevice() const { return false; } @@ -266730,13 +265936,13 @@ public: CGContextTranslateCTM (context, x, -y); } - bool reduceClipRegion (int x, int y, int w, int h) + bool clipToRectangle (const Rectangle& r) { - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); return ! isClipEmpty(); } - bool reduceClipRegion (const RectangleList& clipRegion) + bool clipToRectangleList (const RectangleList& clipRegion) { const int numRects = clipRegion.getNumRectangles(); CGRect* const rects = new CGRect [numRects]; @@ -266752,11 +265958,59 @@ public: return ! isClipEmpty(); } - void excludeClipRegion (int x, int y, int w, int h) + void excludeClipRectangle (const Rectangle& r) { - RectangleList r (getClipBounds()); - r.subtract (Rectangle (x, y, w, h)); - reduceClipRegion (r); + RectangleList remaining (getClipBounds()); + remaining.subtract (r); + clipToRectangleList (remaining); + } + + void clipToPath (const Path& path, const AffineTransform& transform) + { + createPath (path, transform); + CGContextClip (context); + } + + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) + { + if (! transform.isSingularity()) + { + Image* singleChannelImage = createAlphaChannelImage (sourceImage); + CGImageRef image = createImage (*singleChannelImage, true); + + flip(); + AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + applyTransform (t); + + CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + CGContextClipToMask (context, r, image); + + applyTransform (t.inverted()); + flip(); + + CGImageRelease (image); + deleteAlphaChannelImage (sourceImage, singleChannelImage); + } + } + + bool clipRegionIntersects (const Rectangle& r) + { + return getClipBounds().intersects (r); + } + + const Rectangle getClipBounds() const + { + CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); + + return Rectangle (roundFloatToInt (bounds.origin.x), + roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)), + roundFloatToInt (bounds.size.width), + roundFloatToInt (bounds.size.height)); + } + + bool isClipEmpty() const + { + return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); } void saveState() @@ -266783,30 +266037,9 @@ public: } } - bool clipRegionIntersects (int x, int y, int w, int h) - { - return getClipBounds().intersects (Rectangle (x, y, w, h)); - } - - const Rectangle getClipBounds() const - { - CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); - - return Rectangle (roundFloatToInt (bounds.origin.x), - roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)), - roundFloatToInt (bounds.size.width), - roundFloatToInt (bounds.size.height)); - } - - bool isClipEmpty() const - { - return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); - } - void setColour (const Colour& colour) { - state->colour = colour; - deleteAndZero (state->gradient); + state->fillType.setColour (colour); CGContextSetRGBFillColor (context, colour.getFloatRed(), colour.getFloatGreen(), @@ -266816,15 +266049,17 @@ public: void setGradient (const ColourGradient& gradient) { - if (state->gradient == 0) - state->gradient = new ColourGradient (gradient); - else - *state->gradient = gradient; + state->fillType.setGradient (gradient); + } + + void setTiledFill (const Image& image, int x, int y) + { + state->fillType.setTiledImage (image, x, y); } void setOpacity (float opacity) { - setColour (state->colour.withAlpha (opacity)); + state->fillType.colour = state->fillType.colour.withAlpha (opacity); } void setInterpolationQuality (Graphics::ResamplingQuality quality) @@ -266834,36 +266069,46 @@ public: : kCGInterpolationHigh); } - void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) + void fillRect (const Rectangle& r, const bool replaceExistingContents) { + CGRect cgRect = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()); + if (replaceExistingContents) { #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 - CGContextClearRect (context, CGRectMake (x, y, w, h)); + CGContextClearRect (context, cgRect); #else #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 if (CGContextDrawLinearGradient == 0) // (just a way of checking whether we're running in 10.5 or later) - CGContextClearRect (context, CGRectMake (x, y, w, h)); + CGContextClearRect (context, cgRect); else #endif CGContextSetBlendMode (context, kCGBlendModeCopy); #endif - fillRect (x, y, w, h, false); + fillRect (r, false); CGContextSetBlendMode (context, kCGBlendModeNormal); } else { - if (state->gradient == 0) + if (state->fillType.isColour()) { - CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextFillRect (context, cgRect); + } + else if (state->fillType.isGradient()) + { + CGContextSaveGState (context); + CGContextClipToRect (context, cgRect); + flip(); + drawGradient(); + CGContextRestoreGState (context); } else { CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); - flip(); - drawGradient(); + CGContextClipToRect (context, cgRect); + drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), + AffineTransform::translation (state->fillType.imageX, state->fillType.imageY), true); CGContextRestoreGState (context); } } @@ -266873,7 +266118,7 @@ public: { CGContextSaveGState (context); - if (state->gradient == 0) + if (state->fillType.isColour()) { flip(); applyTransform (transform); @@ -266884,101 +266129,77 @@ public: else CGContextEOFillPath (context); } - else + else if (state->fillType.isGradient()) { createPath (path, transform); CGContextClip (context); flip(); - applyTransform (state->gradient->transform); + applyTransform (state->fillType.gradient->transform); drawGradient(); } + else + { + createPath (path, transform); + CGContextClip (context); + drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), + AffineTransform::translation (state->fillType.imageX, state->fillType.imageY), true); + } CGContextRestoreGState (context); } - void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY) - { - CGContextSaveGState (context); - createPath (path, transform); - CGContextClip (context); - blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0); - CGContextRestoreGState (context); - } - - void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) - { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); - - CGContextSaveGState (context); - CGContextSetAlpha (context, 1.0f); - - CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), - alphaImage.getWidth(), alphaImage.getHeight()); - CGContextClipToMask (context, r, image); - - fillRect (alphaImageX, alphaImageY, alphaImage.getWidth(), alphaImage.getHeight(), false); - - CGContextRestoreGState (context); - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } - - void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY) - { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); - - CGContextSaveGState (context); - CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), - alphaImage.getWidth(), alphaImage.getHeight()); - CGContextClipToMask (context, r, image); - - blendImage (fillerImage, fillerImageX, fillerImageY, - fillerImage.getWidth(), fillerImage.getHeight(), - 0, 0); - - CGContextRestoreGState (context); - - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } - - void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY) - { - CGImageRef image = createImage (sourceImage, false); - - CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (destX, flipHeight - (destY + destH), destW, destH)); - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (destX - sourceX, - flipHeight - ((destY - sourceY) + sourceImage.getHeight()), - sourceImage.getWidth(), - sourceImage.getHeight()), image); - - CGContextRestoreGState (context); - CGImageRelease (image); - } - - void blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) + void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { CGImageRef fullImage = createImage (sourceImage, false); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), - srcClipW, srcClipH)); + CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); CGImageRelease (fullImage); CGContextSaveGState (context); + CGContextSetAlpha (context, state->fillType.colour.getFloatAlpha()); + flip(); applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + CGRect imageRect = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), - sourceImage.getHeight()), image); + if (fillEntireClipAsTiles) + { +#if JUCE_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + CGContextDrawTiledImage (context, imageRect, image); +#else + #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (CGContextDrawTiledImage != 0) + CGContextDrawTiledImage (context, imageRect, image); + else + #endif + { + // Fallback to manually doing a tiled fill on 10.4 + CGRect clip = CGRectIntegral (CGContextGetClipBoundingBox (context)); + const int iw = sourceImage.getWidth(); + const int ih = sourceImage.getHeight(); + + int x = 0, y = 0; + while (x > clip.origin.x) x -= iw; + while (y > clip.origin.y) y -= ih; + + const int right = clip.origin.x + clip.size.width; + const int bottom = clip.origin.y + clip.size.height; + + while (y < bottom) + { + for (int x2 = x; x2 < right; x2 += iw) + CGContextDrawImage (context, CGRectMake (x2, y, iw, ih), image); + + y += ih; + } + } +#endif + } + else + { + CGContextDrawImage (context, imageRect, image); + } CGImageRelease (image); CGContextRestoreGState (context); @@ -266989,8 +266210,8 @@ public: CGContextSetLineCap (context, kCGLineCapSquare); CGContextSetLineWidth (context, 1.0f); CGContextSetRGBStrokeColor (context, - state->colour.getFloatRed(), state->colour.getFloatGreen(), - state->colour.getFloatBlue(), state->colour.getFloatAlpha()); + state->fillType.colour.getFloatRed(), state->fillType.colour.getFloatGreen(), + state->fillType.colour.getFloatBlue(), state->fillType.colour.getFloatAlpha()); CGPoint line[] = { { x1 + 0.5f, flipHeight - (y1 + 0.5f) }, { x2 + 0.5f, flipHeight - (y2 + 0.5f) } }; @@ -267030,40 +266251,46 @@ public: } } - void drawGlyph (int glyphNumber, float x, float y) + const Font getFont() { - if (state->fontRef != 0 && state->gradient == 0) - { - CGGlyph g = glyphNumber; - CGContextShowGlyphsAtPoint (context, x, flipHeight - roundFloatToInt (y), &g, 1); - } - else - { - state->font.renderGlyphIndirectly (*this, glyphNumber, x, y); - } + return state->font; } void drawGlyph (int glyphNumber, const AffineTransform& transform) { - if (state->fontRef != 0) + if (state->fontRef != 0 && state->fillType.isColour()) { - CGContextSaveGState (context); - flip(); - applyTransform (transform); + if (transform.isOnlyTranslation()) + { + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, transform.getTranslationX(), + flipHeight - roundFloatToInt (transform.getTranslationY()), &g, 1); + } + else + { + CGContextSaveGState (context); + flip(); + applyTransform (transform); - CGAffineTransform t = state->fontTransform; - t.d = -t.d; - CGContextSetTextMatrix (context, t); + CGAffineTransform t = state->fontTransform; + t.d = -t.d; + CGContextSetTextMatrix (context, t); - CGGlyph g = glyphNumber; - CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); - CGContextSetTextMatrix (context, state->fontTransform); - CGContextRestoreGState (context); + CGContextSetTextMatrix (context, state->fontTransform); + CGContextRestoreGState (context); + } } else { - state->font.renderGlyphIndirectly (*this, glyphNumber, transform); + Path p; + Font& f = state->font; + f.getTypeface()->getOutlineForGlyph (glyphNumber, p); + + fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()) + .followedBy (transform)); } } @@ -267076,26 +266303,21 @@ private: struct SavedState { SavedState() throw() - : gradient (0), font (1.0f), fontRef (0), - fontTransform (CGAffineTransformIdentity) + : font (1.0f), fontRef (0), fontTransform (CGAffineTransformIdentity) { } SavedState (const SavedState& other) throw() - : colour (other.colour), - gradient (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0), - font (other.font), fontRef (other.fontRef), + : fillType (other.fillType), font (other.font), fontRef (other.fontRef), fontTransform (other.fontTransform) { } ~SavedState() throw() { - delete gradient; } - Colour colour; - ColourGradient* gradient; + Graphics::FillType fillType; Font font; CGFontRef fontRef; CGAffineTransform fontTransform; @@ -267103,21 +266325,31 @@ private: SavedState* state; OwnedArray stateStack; + PixelARGB* gradientLookupTable; + int numGradientLookupEntries; static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) { - const ColourGradient* const g = (const ColourGradient*) info; - const Colour c (g->getColourAtPosition (inData[0])); - outData[0] = c.getFloatRed(); - outData[1] = c.getFloatGreen(); - outData[2] = c.getFloatBlue(); - outData[3] = c.getFloatAlpha(); + const CoreGraphicsContext* const g = (const CoreGraphicsContext*) info; + + const int index = roundFloatToInt (g->numGradientLookupEntries * inData[0]); + PixelARGB colour (g->gradientLookupTable [jlimit (0, g->numGradientLookupEntries, index)]); + colour.unpremultiply(); + + outData[0] = colour.getRed() / 255.0f; + outData[1] = colour.getGreen() / 255.0f; + outData[2] = colour.getBlue() / 255.0f; + outData[3] = colour.getAlpha() / 255.0f; } - CGShadingRef createGradient (const ColourGradient* const gradient) const throw() + CGShadingRef createGradient (const ColourGradient* const gradient) throw() { + delete gradientLookupTable; + gradientLookupTable = gradient->createLookupTable (numGradientLookupEntries); + --numGradientLookupEntries; + CGShadingRef result = 0; - CGFunctionRef function = CGFunctionCreate ((void*) gradient, 1, 0, 4, 0, &gradientCallbacks); + CGFunctionRef function = CGFunctionCreate ((void*) this, 1, 0, 4, 0, &gradientCallbacks); CGPoint p1 (CGPointMake (gradient->x1, gradient->y1)); if (gradient->isRadial) @@ -267137,12 +266369,12 @@ private: return result; } - void drawGradient() const throw() + void drawGradient() throw() { CGContextSetAlpha (context, 1.0f); CGContextSetInterpolationQuality (context, kCGInterpolationDefault); // (This is required for 10.4, where there's a crash if // you draw a gradient with high quality interp enabled). - CGShadingRef shading = createGradient (state->gradient); + CGShadingRef shading = createGradient (state->fillType.gradient); CGContextDrawShading (context, shading); CGShadingRelease (shading); } @@ -268715,7 +267947,7 @@ void NSViewComponentPeer::drawRect (NSRect r) roundFloatToInt (rects[i].size.height))); } - if (context.reduceClipRegion (clip)) + if (context.clipToRectangleList (clip)) { insideDrawRect = true; handlePaint (context); diff --git a/juce_amalgamated.h b/juce_amalgamated.h index c1bd212cb0..63b1aeeddd 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -7107,98 +7107,6 @@ void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI(); #endif #ifndef __JUCE_LOGGER_JUCEHEADER__ -#endif -#ifndef __JUCE_MATHSFUNCTIONS_JUCEHEADER__ - -#endif -#ifndef __JUCE_MEMORY_JUCEHEADER__ - -#endif -#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ - -/********* Start of inlined file: juce_PerformanceCounter.h *********/ -#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ -#define __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ - -/** A timer for measuring performance of code and dumping the results to a file. - - e.g. @code - - PerformanceCounter pc ("fish", 50, "/temp/myfishlog.txt"); - - for (;;) - { - pc.start(); - - doSomethingFishy(); - - pc.stop(); - } - @endcode - - In this example, the time of each period between calling start/stop will be - measured and averaged over 50 runs, and the results printed to a file - every 50 times round the loop. -*/ -class JUCE_API PerformanceCounter -{ -public: - - /** Creates a PerformanceCounter object. - - @param counterName the name used when printing out the statistics - @param runsPerPrintout the number of start/stop iterations before calling - printStatistics() - @param loggingFile a file to dump the results to - if this is File::nonexistent, - the results are just written to the debugger output - */ - PerformanceCounter (const String& counterName, - int runsPerPrintout = 100, - const File& loggingFile = File::nonexistent); - - /** Destructor. */ - ~PerformanceCounter(); - - /** Starts timing. - - @see stop - */ - void start(); - - /** Stops timing and prints out the results. - - The number of iterations before doing a printout of the - results is set in the constructor. - - @see start - */ - void stop(); - - /** Dumps the current metrics to the debugger output and to a file. - - As well as using Logger::outputDebugString to print the results, - this will write then to the file specified in the constructor (if - this was valid). - */ - void printStatistics(); - - juce_UseDebuggingNewOperator - -private: - - String name; - int numRuns, runsPerPrint; - double totalTime; - int64 started; - File outputFile; -}; - -#endif // __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ -/********* End of inlined file: juce_PerformanceCounter.h *********/ - -#endif -#ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ - #endif #ifndef __JUCE_PLATFORMUTILITIES_JUCEHEADER__ @@ -7470,6 +7378,485 @@ private: #endif // __JUCE_PLATFORMUTILITIES_JUCEHEADER__ /********* End of inlined file: juce_PlatformUtilities.h *********/ +#endif +#ifndef __JUCE_MEMORY_JUCEHEADER__ + +#endif +#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ + +/********* Start of inlined file: juce_PerformanceCounter.h *********/ +#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ +#define __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ + +/** A timer for measuring performance of code and dumping the results to a file. + + e.g. @code + + PerformanceCounter pc ("fish", 50, "/temp/myfishlog.txt"); + + for (;;) + { + pc.start(); + + doSomethingFishy(); + + pc.stop(); + } + @endcode + + In this example, the time of each period between calling start/stop will be + measured and averaged over 50 runs, and the results printed to a file + every 50 times round the loop. +*/ +class JUCE_API PerformanceCounter +{ +public: + + /** Creates a PerformanceCounter object. + + @param counterName the name used when printing out the statistics + @param runsPerPrintout the number of start/stop iterations before calling + printStatistics() + @param loggingFile a file to dump the results to - if this is File::nonexistent, + the results are just written to the debugger output + */ + PerformanceCounter (const String& counterName, + int runsPerPrintout = 100, + const File& loggingFile = File::nonexistent); + + /** Destructor. */ + ~PerformanceCounter(); + + /** Starts timing. + + @see stop + */ + void start(); + + /** Stops timing and prints out the results. + + The number of iterations before doing a printout of the + results is set in the constructor. + + @see start + */ + void stop(); + + /** Dumps the current metrics to the debugger output and to a file. + + As well as using Logger::outputDebugString to print the results, + this will write then to the file specified in the constructor (if + this was valid). + */ + void printStatistics(); + + juce_UseDebuggingNewOperator + +private: + + String name; + int numRuns, runsPerPrint; + double totalTime; + int64 started; + File outputFile; +}; + +#endif // __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ +/********* End of inlined file: juce_PerformanceCounter.h *********/ + +#endif +#ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ + +#endif +#ifndef __JUCE_SINGLETON_JUCEHEADER__ + +/********* Start of inlined file: juce_Singleton.h *********/ +#ifndef __JUCE_SINGLETON_JUCEHEADER__ +#define __JUCE_SINGLETON_JUCEHEADER__ + +/********* Start of inlined file: juce_ScopedLock.h *********/ +#ifndef __JUCE_SCOPEDLOCK_JUCEHEADER__ +#define __JUCE_SCOPEDLOCK_JUCEHEADER__ + +/** + Automatically locks and unlocks a CriticalSection object. + + Use one of these as a local variable to control access to a CriticalSection. + + e.g. @code + + CriticalSection myCriticalSection; + + for (;;) + { + const ScopedLock myScopedLock (myCriticalSection); + // myCriticalSection is now locked + + ...do some stuff... + + // myCriticalSection gets unlocked here. + } + @endcode + + @see CriticalSection, ScopedUnlock +*/ +class JUCE_API ScopedLock +{ +public: + + /** Creates a ScopedLock. + + As soon as it is created, this will lock the CriticalSection, and + when the ScopedLock object is deleted, the CriticalSection will + be unlocked. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! Best just to use it + as a local stack object, rather than creating one with the new() operator. + */ + inline ScopedLock (const CriticalSection& lock) throw() : lock_ (lock) { lock.enter(); } + + /** Destructor. + + The CriticalSection will be unlocked when the destructor is called. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + inline ~ScopedLock() throw() { lock_.exit(); } + +private: + + const CriticalSection& lock_; + + ScopedLock (const ScopedLock&); + const ScopedLock& operator= (const ScopedLock&); +}; + +/** + Automatically unlocks and re-locks a CriticalSection object. + + This is the reverse of a ScopedLock object - instead of locking the critical + section for the lifetime of this object, it unlocks it. + + Make sure you don't try to unlock critical sections that aren't actually locked! + + e.g. @code + + CriticalSection myCriticalSection; + + for (;;) + { + const ScopedLock myScopedLock (myCriticalSection); + // myCriticalSection is now locked + + ... do some stuff with it locked .. + + while (xyz) + { + ... do some stuff with it locked .. + + const ScopedUnlock unlocker (myCriticalSection); + + // myCriticalSection is now unlocked for the remainder of this block, + // and re-locked at the end. + + ...do some stuff with it unlocked ... + } + + // myCriticalSection gets unlocked here. + } + @endcode + + @see CriticalSection, ScopedLock +*/ +class ScopedUnlock +{ +public: + + /** Creates a ScopedUnlock. + + As soon as it is created, this will unlock the CriticalSection, and + when the ScopedLock object is deleted, the CriticalSection will + be re-locked. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! Best just to use it + as a local stack object, rather than creating one with the new() operator. + */ + inline ScopedUnlock (const CriticalSection& lock) throw() : lock_ (lock) { lock.exit(); } + + /** Destructor. + + The CriticalSection will be unlocked when the destructor is called. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + inline ~ScopedUnlock() throw() { lock_.enter(); } + +private: + + const CriticalSection& lock_; + + ScopedUnlock (const ScopedLock&); + const ScopedUnlock& operator= (const ScopedUnlock&); +}; + +#endif // __JUCE_SCOPEDLOCK_JUCEHEADER__ +/********* End of inlined file: juce_ScopedLock.h *********/ + +/** + Macro to declare member variables and methods for a singleton class. + + To use this, add the line juce_DeclareSingleton (MyClass, doNotRecreateAfterDeletion) + to the class's definition. + + Then put a macro juce_ImplementSingleton (MyClass) along with the class's + implementation code. + + It's also a very good idea to also add the call clearSingletonInstance() in your class's + destructor, in case it is deleted by other means than deleteInstance() + + Clients can then call the static method MyClass::getInstance() to get a pointer + to the singleton, or MyClass::getInstanceWithoutCreating() which will return 0 if + no instance currently exists. + + e.g. @code + + class MySingleton + { + public: + MySingleton() + { + } + + ~MySingleton() + { + // this ensures that no dangling pointers are left when the + // singleton is deleted. + clearSingletonInstance(); + } + + juce_DeclareSingleton (MySingleton, false) + }; + + juce_ImplementSingleton (MySingleton) + + // example of usage: + MySingleton* m = MySingleton::getInstance(); // creates the singleton if there isn't already one. + + ... + + MySingleton::deleteInstance(); // safely deletes the singleton (if it's been created). + + @endcode + + If doNotRecreateAfterDeletion = true, it won't allow the object to be created more + than once during the process's lifetime - i.e. after you've created and deleted the + object, getInstance() will refuse to create another one. This can be useful to stop + objects being accidentally re-created during your app's shutdown code. + + If you know that your object will only be created and deleted by a single thread, you + can use the slightly more efficient juce_DeclareSingleton_SingleThreaded() macro instead + of this one. + + @see juce_ImplementSingleton, juce_DeclareSingleton_SingleThreaded +*/ +#define juce_DeclareSingleton(classname, doNotRecreateAfterDeletion) \ +\ + static classname* _singletonInstance; \ + static JUCE_NAMESPACE::CriticalSection _singletonLock; \ +\ + static classname* getInstance() \ + { \ + if (_singletonInstance == 0) \ + {\ + const JUCE_NAMESPACE::ScopedLock sl (_singletonLock); \ +\ + if (_singletonInstance == 0) \ + { \ + static bool alreadyInside = false; \ + static bool createdOnceAlready = false; \ +\ + const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ + jassert (! problem); \ + if (! problem) \ + { \ + createdOnceAlready = true; \ + alreadyInside = true; \ + classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ + alreadyInside = false; \ +\ + _singletonInstance = newObject; \ + } \ + } \ + } \ +\ + return _singletonInstance; \ + } \ +\ + static inline classname* getInstanceWithoutCreating() throw() \ + { \ + return _singletonInstance; \ + } \ +\ + static void deleteInstance() \ + { \ + const JUCE_NAMESPACE::ScopedLock sl (_singletonLock); \ + if (_singletonInstance != 0) \ + { \ + classname* const old = _singletonInstance; \ + _singletonInstance = 0; \ + delete old; \ + } \ + } \ +\ + void clearSingletonInstance() throw() \ + { \ + if (_singletonInstance == this) \ + _singletonInstance = 0; \ + } + +/** This is a counterpart to the juce_DeclareSingleton macro. + + After adding the juce_DeclareSingleton to the class definition, this macro has + to be used in the cpp file. +*/ +#define juce_ImplementSingleton(classname) \ +\ + classname* classname::_singletonInstance = 0; \ + JUCE_NAMESPACE::CriticalSection classname::_singletonLock; + +/** + Macro to declare member variables and methods for a singleton class. + + This is exactly the same as juce_DeclareSingleton, but doesn't use a critical + section to make access to it thread-safe. If you know that your object will + only ever be created or deleted by a single thread, then this is a + more efficient version to use. + + If doNotRecreateAfterDeletion = true, it won't allow the object to be created more + than once during the process's lifetime - i.e. after you've created and deleted the + object, getInstance() will refuse to create another one. This can be useful to stop + objects being accidentally re-created during your app's shutdown code. + + See the documentation for juce_DeclareSingleton for more information about + how to use it, the only difference being that you have to use + juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. + + @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton, juce_DeclareSingleton_SingleThreaded_Minimal +*/ +#define juce_DeclareSingleton_SingleThreaded(classname, doNotRecreateAfterDeletion) \ +\ + static classname* _singletonInstance; \ +\ + static classname* getInstance() \ + { \ + if (_singletonInstance == 0) \ + { \ + static bool alreadyInside = false; \ + static bool createdOnceAlready = false; \ +\ + const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ + jassert (! problem); \ + if (! problem) \ + { \ + createdOnceAlready = true; \ + alreadyInside = true; \ + classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ + alreadyInside = false; \ +\ + _singletonInstance = newObject; \ + } \ + } \ +\ + return _singletonInstance; \ + } \ +\ + static inline classname* getInstanceWithoutCreating() throw() \ + { \ + return _singletonInstance; \ + } \ +\ + static void deleteInstance() \ + { \ + if (_singletonInstance != 0) \ + { \ + classname* const old = _singletonInstance; \ + _singletonInstance = 0; \ + delete old; \ + } \ + } \ +\ + void clearSingletonInstance() throw() \ + { \ + if (_singletonInstance == this) \ + _singletonInstance = 0; \ + } + +/** + Macro to declare member variables and methods for a singleton class. + + This is like juce_DeclareSingleton_SingleThreaded, but doesn't do any checking + for recursion or repeated instantiation. It's intended for use as a lightweight + version of a singleton, where you're using it in very straightforward + circumstances and don't need the extra checking. + + Juce use the normal juce_ImplementSingleton_SingleThreaded as the counterpart + to this declaration, as you would with juce_DeclareSingleton_SingleThreaded. + + See the documentation for juce_DeclareSingleton for more information about + how to use it, the only difference being that you have to use + juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. + + @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton +*/ +#define juce_DeclareSingleton_SingleThreaded_Minimal(classname) \ +\ + static classname* _singletonInstance; \ +\ + static classname* getInstance() \ + { \ + if (_singletonInstance == 0) \ + _singletonInstance = new classname(); \ +\ + return _singletonInstance; \ + } \ +\ + static inline classname* getInstanceWithoutCreating() throw() \ + { \ + return _singletonInstance; \ + } \ +\ + static void deleteInstance() \ + { \ + if (_singletonInstance != 0) \ + { \ + classname* const old = _singletonInstance; \ + _singletonInstance = 0; \ + delete old; \ + } \ + } \ +\ + void clearSingletonInstance() throw() \ + { \ + if (_singletonInstance == this) \ + _singletonInstance = 0; \ + } + +/** This is a counterpart to the juce_DeclareSingleton_SingleThreaded macro. + + After adding juce_DeclareSingleton_SingleThreaded or juce_DeclareSingleton_SingleThreaded_Minimal + to the class definition, this macro has to be used somewhere in the cpp file. +*/ +#define juce_ImplementSingleton_SingleThreaded(classname) \ +\ + classname* classname::_singletonInstance = 0; + +#endif // __JUCE_SINGLETON_JUCEHEADER__ +/********* End of inlined file: juce_Singleton.h *********/ + #endif #ifndef __JUCE_RANDOM_JUCEHEADER__ @@ -7878,399 +8265,6 @@ private: #endif #ifndef __JUCE_RELATIVETIME_JUCEHEADER__ -#endif -#ifndef __JUCE_SINGLETON_JUCEHEADER__ - -/********* Start of inlined file: juce_Singleton.h *********/ -#ifndef __JUCE_SINGLETON_JUCEHEADER__ -#define __JUCE_SINGLETON_JUCEHEADER__ - -/********* Start of inlined file: juce_ScopedLock.h *********/ -#ifndef __JUCE_SCOPEDLOCK_JUCEHEADER__ -#define __JUCE_SCOPEDLOCK_JUCEHEADER__ - -/** - Automatically locks and unlocks a CriticalSection object. - - Use one of these as a local variable to control access to a CriticalSection. - - e.g. @code - - CriticalSection myCriticalSection; - - for (;;) - { - const ScopedLock myScopedLock (myCriticalSection); - // myCriticalSection is now locked - - ...do some stuff... - - // myCriticalSection gets unlocked here. - } - @endcode - - @see CriticalSection, ScopedUnlock -*/ -class JUCE_API ScopedLock -{ -public: - - /** Creates a ScopedLock. - - As soon as it is created, this will lock the CriticalSection, and - when the ScopedLock object is deleted, the CriticalSection will - be unlocked. - - Make sure this object is created and deleted by the same thread, - otherwise there are no guarantees what will happen! Best just to use it - as a local stack object, rather than creating one with the new() operator. - */ - inline ScopedLock (const CriticalSection& lock) throw() : lock_ (lock) { lock.enter(); } - - /** Destructor. - - The CriticalSection will be unlocked when the destructor is called. - - Make sure this object is created and deleted by the same thread, - otherwise there are no guarantees what will happen! - */ - inline ~ScopedLock() throw() { lock_.exit(); } - -private: - - const CriticalSection& lock_; - - ScopedLock (const ScopedLock&); - const ScopedLock& operator= (const ScopedLock&); -}; - -/** - Automatically unlocks and re-locks a CriticalSection object. - - This is the reverse of a ScopedLock object - instead of locking the critical - section for the lifetime of this object, it unlocks it. - - Make sure you don't try to unlock critical sections that aren't actually locked! - - e.g. @code - - CriticalSection myCriticalSection; - - for (;;) - { - const ScopedLock myScopedLock (myCriticalSection); - // myCriticalSection is now locked - - ... do some stuff with it locked .. - - while (xyz) - { - ... do some stuff with it locked .. - - const ScopedUnlock unlocker (myCriticalSection); - - // myCriticalSection is now unlocked for the remainder of this block, - // and re-locked at the end. - - ...do some stuff with it unlocked ... - } - - // myCriticalSection gets unlocked here. - } - @endcode - - @see CriticalSection, ScopedLock -*/ -class ScopedUnlock -{ -public: - - /** Creates a ScopedUnlock. - - As soon as it is created, this will unlock the CriticalSection, and - when the ScopedLock object is deleted, the CriticalSection will - be re-locked. - - Make sure this object is created and deleted by the same thread, - otherwise there are no guarantees what will happen! Best just to use it - as a local stack object, rather than creating one with the new() operator. - */ - inline ScopedUnlock (const CriticalSection& lock) throw() : lock_ (lock) { lock.exit(); } - - /** Destructor. - - The CriticalSection will be unlocked when the destructor is called. - - Make sure this object is created and deleted by the same thread, - otherwise there are no guarantees what will happen! - */ - inline ~ScopedUnlock() throw() { lock_.enter(); } - -private: - - const CriticalSection& lock_; - - ScopedUnlock (const ScopedLock&); - const ScopedUnlock& operator= (const ScopedUnlock&); -}; - -#endif // __JUCE_SCOPEDLOCK_JUCEHEADER__ -/********* End of inlined file: juce_ScopedLock.h *********/ - -/** - Macro to declare member variables and methods for a singleton class. - - To use this, add the line juce_DeclareSingleton (MyClass, doNotRecreateAfterDeletion) - to the class's definition. - - Then put a macro juce_ImplementSingleton (MyClass) along with the class's - implementation code. - - It's also a very good idea to also add the call clearSingletonInstance() in your class's - destructor, in case it is deleted by other means than deleteInstance() - - Clients can then call the static method MyClass::getInstance() to get a pointer - to the singleton, or MyClass::getInstanceWithoutCreating() which will return 0 if - no instance currently exists. - - e.g. @code - - class MySingleton - { - public: - MySingleton() - { - } - - ~MySingleton() - { - // this ensures that no dangling pointers are left when the - // singleton is deleted. - clearSingletonInstance(); - } - - juce_DeclareSingleton (MySingleton, false) - }; - - juce_ImplementSingleton (MySingleton) - - // example of usage: - MySingleton* m = MySingleton::getInstance(); // creates the singleton if there isn't already one. - - ... - - MySingleton::deleteInstance(); // safely deletes the singleton (if it's been created). - - @endcode - - If doNotRecreateAfterDeletion = true, it won't allow the object to be created more - than once during the process's lifetime - i.e. after you've created and deleted the - object, getInstance() will refuse to create another one. This can be useful to stop - objects being accidentally re-created during your app's shutdown code. - - If you know that your object will only be created and deleted by a single thread, you - can use the slightly more efficient juce_DeclareSingleton_SingleThreaded() macro instead - of this one. - - @see juce_ImplementSingleton, juce_DeclareSingleton_SingleThreaded -*/ -#define juce_DeclareSingleton(classname, doNotRecreateAfterDeletion) \ -\ - static classname* _singletonInstance; \ - static JUCE_NAMESPACE::CriticalSection _singletonLock; \ -\ - static classname* getInstance() \ - { \ - if (_singletonInstance == 0) \ - {\ - const JUCE_NAMESPACE::ScopedLock sl (_singletonLock); \ -\ - if (_singletonInstance == 0) \ - { \ - static bool alreadyInside = false; \ - static bool createdOnceAlready = false; \ -\ - const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ - jassert (! problem); \ - if (! problem) \ - { \ - createdOnceAlready = true; \ - alreadyInside = true; \ - classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ - alreadyInside = false; \ -\ - _singletonInstance = newObject; \ - } \ - } \ - } \ -\ - return _singletonInstance; \ - } \ -\ - static inline classname* getInstanceWithoutCreating() throw() \ - { \ - return _singletonInstance; \ - } \ -\ - static void deleteInstance() \ - { \ - const JUCE_NAMESPACE::ScopedLock sl (_singletonLock); \ - if (_singletonInstance != 0) \ - { \ - classname* const old = _singletonInstance; \ - _singletonInstance = 0; \ - delete old; \ - } \ - } \ -\ - void clearSingletonInstance() throw() \ - { \ - if (_singletonInstance == this) \ - _singletonInstance = 0; \ - } - -/** This is a counterpart to the juce_DeclareSingleton macro. - - After adding the juce_DeclareSingleton to the class definition, this macro has - to be used in the cpp file. -*/ -#define juce_ImplementSingleton(classname) \ -\ - classname* classname::_singletonInstance = 0; \ - JUCE_NAMESPACE::CriticalSection classname::_singletonLock; - -/** - Macro to declare member variables and methods for a singleton class. - - This is exactly the same as juce_DeclareSingleton, but doesn't use a critical - section to make access to it thread-safe. If you know that your object will - only ever be created or deleted by a single thread, then this is a - more efficient version to use. - - If doNotRecreateAfterDeletion = true, it won't allow the object to be created more - than once during the process's lifetime - i.e. after you've created and deleted the - object, getInstance() will refuse to create another one. This can be useful to stop - objects being accidentally re-created during your app's shutdown code. - - See the documentation for juce_DeclareSingleton for more information about - how to use it, the only difference being that you have to use - juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. - - @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton, juce_DeclareSingleton_SingleThreaded_Minimal -*/ -#define juce_DeclareSingleton_SingleThreaded(classname, doNotRecreateAfterDeletion) \ -\ - static classname* _singletonInstance; \ -\ - static classname* getInstance() \ - { \ - if (_singletonInstance == 0) \ - { \ - static bool alreadyInside = false; \ - static bool createdOnceAlready = false; \ -\ - const bool problem = alreadyInside || ((doNotRecreateAfterDeletion) && createdOnceAlready); \ - jassert (! problem); \ - if (! problem) \ - { \ - createdOnceAlready = true; \ - alreadyInside = true; \ - classname* newObject = new classname(); /* (use a stack variable to avoid setting the newObject value before the class has finished its constructor) */ \ - alreadyInside = false; \ -\ - _singletonInstance = newObject; \ - } \ - } \ -\ - return _singletonInstance; \ - } \ -\ - static inline classname* getInstanceWithoutCreating() throw() \ - { \ - return _singletonInstance; \ - } \ -\ - static void deleteInstance() \ - { \ - if (_singletonInstance != 0) \ - { \ - classname* const old = _singletonInstance; \ - _singletonInstance = 0; \ - delete old; \ - } \ - } \ -\ - void clearSingletonInstance() throw() \ - { \ - if (_singletonInstance == this) \ - _singletonInstance = 0; \ - } - -/** - Macro to declare member variables and methods for a singleton class. - - This is like juce_DeclareSingleton_SingleThreaded, but doesn't do any checking - for recursion or repeated instantiation. It's intended for use as a lightweight - version of a singleton, where you're using it in very straightforward - circumstances and don't need the extra checking. - - Juce use the normal juce_ImplementSingleton_SingleThreaded as the counterpart - to this declaration, as you would with juce_DeclareSingleton_SingleThreaded. - - See the documentation for juce_DeclareSingleton for more information about - how to use it, the only difference being that you have to use - juce_ImplementSingleton_SingleThreaded instead of juce_ImplementSingleton. - - @see juce_ImplementSingleton_SingleThreaded, juce_DeclareSingleton -*/ -#define juce_DeclareSingleton_SingleThreaded_Minimal(classname) \ -\ - static classname* _singletonInstance; \ -\ - static classname* getInstance() \ - { \ - if (_singletonInstance == 0) \ - _singletonInstance = new classname(); \ -\ - return _singletonInstance; \ - } \ -\ - static inline classname* getInstanceWithoutCreating() throw() \ - { \ - return _singletonInstance; \ - } \ -\ - static void deleteInstance() \ - { \ - if (_singletonInstance != 0) \ - { \ - classname* const old = _singletonInstance; \ - _singletonInstance = 0; \ - delete old; \ - } \ - } \ -\ - void clearSingletonInstance() throw() \ - { \ - if (_singletonInstance == this) \ - _singletonInstance = 0; \ - } - -/** This is a counterpart to the juce_DeclareSingleton_SingleThreaded macro. - - After adding juce_DeclareSingleton_SingleThreaded or juce_DeclareSingleton_SingleThreaded_Minimal - to the class definition, this macro has to be used somewhere in the cpp file. -*/ -#define juce_ImplementSingleton_SingleThreaded(classname) \ -\ - classname* classname::_singletonInstance = 0; - -#endif // __JUCE_SINGLETON_JUCEHEADER__ -/********* End of inlined file: juce_Singleton.h *********/ - -#endif -#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ - #endif #ifndef __JUCE_SYSTEMSTATS_JUCEHEADER__ @@ -8418,9 +8412,6 @@ public: #endif // __JUCE_SYSTEMSTATS_JUCEHEADER__ /********* End of inlined file: juce_SystemStats.h *********/ -#endif -#ifndef __JUCE_TARGETPLATFORM_JUCEHEADER__ - #endif #ifndef __JUCE_TIME_JUCEHEADER__ @@ -8517,6 +8508,15 @@ private: #endif // __JUCE_UUID_JUCEHEADER__ /********* End of inlined file: juce_Uuid.h *********/ +#endif +#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ + +#endif +#ifndef __JUCE_MATHSFUNCTIONS_JUCEHEADER__ + +#endif +#ifndef __JUCE_TARGETPLATFORM_JUCEHEADER__ + #endif #ifndef __JUCE_ARRAY_JUCEHEADER__ @@ -15026,6 +15026,116 @@ private: #endif #ifndef __JUCE_THREAD_JUCEHEADER__ +#endif +#ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ + +/********* Start of inlined file: juce_TimeSliceThread.h *********/ +#ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ +#define __JUCE_TIMESLICETHREAD_JUCEHEADER__ + +/** + Used by the TimeSliceThread class. + + To register your class with a TimeSliceThread, derive from this class and + use the TimeSliceThread::addTimeSliceClient() method to add it to the list. + + Make sure you always call TimeSliceThread::removeTimeSliceClient() before + deleting your client! + + @see TimeSliceThread +*/ +class JUCE_API TimeSliceClient +{ +public: + /** Destructor. */ + virtual ~TimeSliceClient() {} + + /** Called back by a TimeSliceThread. + + When you register this class with it, a TimeSliceThread will repeatedly call + this method. + + The implementation of this method should use its time-slice to do something that's + quick - never block for longer than absolutely necessary. + + @returns Your method should return true if it needs more time, or false if it's + not too busy and doesn't need calling back urgently. If all the thread's + clients indicate that they're not busy, then it'll save CPU by sleeping for + up to half a second in between callbacks. You can force the TimeSliceThread + to wake up and poll again immediately by calling its notify() method. + */ + virtual bool useTimeSlice() = 0; +}; + +/** + A thread that keeps a list of clients, and calls each one in turn, giving them + all a chance to run some sort of short task. + + @see TimeSliceClient, Thread +*/ +class JUCE_API TimeSliceThread : public Thread +{ +public: + + /** + Creates a TimeSliceThread. + + When first created, the thread is not running. Use the startThread() + method to start it. + */ + TimeSliceThread (const String& threadName); + + /** Destructor. + + Deleting a Thread object that is running will only give the thread a + brief opportunity to stop itself cleanly, so it's recommended that you + should always call stopThread() with a decent timeout before deleting, + to avoid the thread being forcibly killed (which is a Bad Thing). + */ + ~TimeSliceThread(); + + /** Adds a client to the list. + + The client's callbacks will start immediately (possibly before the method + has returned). + */ + void addTimeSliceClient (TimeSliceClient* const client); + + /** Removes a client from the list. + + This method will make sure that all callbacks to the client have completely + finished before the method returns. + */ + void removeTimeSliceClient (TimeSliceClient* const client); + + /** Returns the number of registered clients. */ + int getNumClients() const throw(); + + /** Returns one of the registered clients. */ + TimeSliceClient* getClient (const int index) const throw(); + + /** @internal */ + void run(); + + juce_UseDebuggingNewOperator + +private: + CriticalSection callbackLock, listLock; + Array clients; + int index; + TimeSliceClient* clientBeingCalled; + bool clientsChanged; + + TimeSliceThread (const TimeSliceThread&); + const TimeSliceThread& operator= (const TimeSliceThread&); +}; + +#endif // __JUCE_TIMESLICETHREAD_JUCEHEADER__ +/********* End of inlined file: juce_TimeSliceThread.h *********/ + +#endif +#ifndef __JUCE_WAITABLEEVENT_JUCEHEADER__ + #endif #ifndef __JUCE_THREADPOOL_JUCEHEADER__ @@ -15286,116 +15396,6 @@ private: #endif // __JUCE_THREADPOOL_JUCEHEADER__ /********* End of inlined file: juce_ThreadPool.h *********/ -#endif -#ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ - -/********* Start of inlined file: juce_TimeSliceThread.h *********/ -#ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ -#define __JUCE_TIMESLICETHREAD_JUCEHEADER__ - -/** - Used by the TimeSliceThread class. - - To register your class with a TimeSliceThread, derive from this class and - use the TimeSliceThread::addTimeSliceClient() method to add it to the list. - - Make sure you always call TimeSliceThread::removeTimeSliceClient() before - deleting your client! - - @see TimeSliceThread -*/ -class JUCE_API TimeSliceClient -{ -public: - /** Destructor. */ - virtual ~TimeSliceClient() {} - - /** Called back by a TimeSliceThread. - - When you register this class with it, a TimeSliceThread will repeatedly call - this method. - - The implementation of this method should use its time-slice to do something that's - quick - never block for longer than absolutely necessary. - - @returns Your method should return true if it needs more time, or false if it's - not too busy and doesn't need calling back urgently. If all the thread's - clients indicate that they're not busy, then it'll save CPU by sleeping for - up to half a second in between callbacks. You can force the TimeSliceThread - to wake up and poll again immediately by calling its notify() method. - */ - virtual bool useTimeSlice() = 0; -}; - -/** - A thread that keeps a list of clients, and calls each one in turn, giving them - all a chance to run some sort of short task. - - @see TimeSliceClient, Thread -*/ -class JUCE_API TimeSliceThread : public Thread -{ -public: - - /** - Creates a TimeSliceThread. - - When first created, the thread is not running. Use the startThread() - method to start it. - */ - TimeSliceThread (const String& threadName); - - /** Destructor. - - Deleting a Thread object that is running will only give the thread a - brief opportunity to stop itself cleanly, so it's recommended that you - should always call stopThread() with a decent timeout before deleting, - to avoid the thread being forcibly killed (which is a Bad Thing). - */ - ~TimeSliceThread(); - - /** Adds a client to the list. - - The client's callbacks will start immediately (possibly before the method - has returned). - */ - void addTimeSliceClient (TimeSliceClient* const client); - - /** Removes a client from the list. - - This method will make sure that all callbacks to the client have completely - finished before the method returns. - */ - void removeTimeSliceClient (TimeSliceClient* const client); - - /** Returns the number of registered clients. */ - int getNumClients() const throw(); - - /** Returns one of the registered clients. */ - TimeSliceClient* getClient (const int index) const throw(); - - /** @internal */ - void run(); - - juce_UseDebuggingNewOperator - -private: - CriticalSection callbackLock, listLock; - Array clients; - int index; - TimeSliceClient* clientBeingCalled; - bool clientsChanged; - - TimeSliceThread (const TimeSliceThread&); - const TimeSliceThread& operator= (const TimeSliceThread&); -}; - -#endif // __JUCE_TIMESLICETHREAD_JUCEHEADER__ -/********* End of inlined file: juce_TimeSliceThread.h *********/ - -#endif -#ifndef __JUCE_WAITABLEEVENT_JUCEHEADER__ - #endif #endif @@ -17181,9 +17181,10 @@ public: void clipToRectangle (const Rectangle& r) throw(); void excludeRectangle (const Rectangle& r) throw(); void clipToEdgeTable (const EdgeTable& other); - void clipToImageAlpha (const Image& image, int x, int y) throw(); - bool isEmpty() const throw(); + void clipLineToMask (int x, int y, uint8* mask, int maskStride, int numPixels) throw(); + bool isEmpty() throw(); const Rectangle& getMaximumBounds() const throw() { return bounds; } + void translate (float dx, int dy) throw(); /** Reduces the amount of space the table has allocated. @@ -17292,6 +17293,7 @@ private: int* table; Rectangle bounds; int maxEdgesPerLine, lineStrideElements; + bool needToCheckEmptinesss; void addEdgePoint (const int x, const int y, const int winding) throw(); void remapTableForNumEdges (const int newNumEdgesPerLine) throw(); @@ -17484,6 +17486,15 @@ public: void addRectangle (const float x, const float y, const float w, const float h) throw(); + /** Adds a rectangle to the path. + + The rectangle is added as a new sub-path. (Any currently open paths will be + left open). + + @see addRoundedRectangle, addTriangle + */ + void addRectangle (const Rectangle& rectangle) throw(); + /** Adds a rectangle with rounded corners to the path. The rectangle is added as a new sub-path. (Any currently open paths will be @@ -17870,21 +17881,6 @@ public: */ void restoreFromString (const String& stringVersion); - /** Creates a single-channel bitmap containing a mask of this path. - - The smallest bitmap that contains the path will be created, and on return, the - imagePosition rectangle indicates the position of the newly created image, relative - to the path's origin. - - Only the intersection of the path's bounds with the specified clipRegion rectangle - will be rendered. - - If the path is empty or doesn't intersect the clip region, this may return 0. - */ - Image* createMaskBitmap (const AffineTransform& transform, - const Rectangle& clipRegion, - Rectangle& imagePosition) const throw(); - juce_UseDebuggingNewOperator private: @@ -18359,23 +18355,6 @@ public: */ void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) const throw(); - /** Renders a glyph in a context without using methods other than the context's glyph-rendering - methods. - - For smaller fonts, this uses an internal cache of glyph images to speed things up, and renders - them using the context's image blending methods. For larger fonts, it gets the glyph's path - from the typeface and renders it as a shape. - - This method is primarily called by graphics contexts as a way of drawing a glyph if they can't do - it by native means. - */ - void renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, float x, float y); - - /** Renders a transformed glyph using path-filling techniques rather than calling a context's - actual glyph-rendering methods. - */ - void renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, const AffineTransform& transform); - /** Returns the typeface used by this font. Note that the object returned may go out of scope if this font is deleted @@ -19852,159 +19831,6 @@ private: #endif // __JUCE_COLOURGRADIENT_JUCEHEADER__ /********* End of inlined file: juce_ColourGradient.h *********/ -/********* Start of inlined file: juce_SolidColourBrush.h *********/ -#ifndef __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ -#define __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ - -/********* Start of inlined file: juce_Brush.h *********/ -#ifndef __JUCE_BRUSH_JUCEHEADER__ -#define __JUCE_BRUSH_JUCEHEADER__ - -class Path; -class AffineTransform; -class LowLevelGraphicsContext; -class Image; -class Graphics; - -/** - A brush is used to fill areas with colours, patterns, or images. - - The Graphics class has an idea of a current brush which it uses to render - shapes, rectangles, lines, text, etc. - - This is the base class - there are subclasses for useful types of fill pattern, - and applications can define their own brushes too. - - @see Graphics::setBrush, SolidColourBrush, GradientBrush, ImageBrush -*/ -class JUCE_API Brush -{ -protected: - - /** Creates a Brush. - - (Nothing much happens in the base class). - */ - Brush() throw(); - -public: - /** Destructor. */ - virtual ~Brush() throw(); - - /** Creates a copy of whatever class of Brush this is. */ - virtual Brush* createCopy() const throw() = 0; - - /** Does whatever is relevent to transform the geometry of this brush. */ - virtual void applyTransform (const AffineTransform& transform) throw() = 0; - - /** Does whatever is relevent to change the opacity of this brush. */ - virtual void multiplyOpacity (const float multiple) throw() = 0; - - /** Must return true if this brush won't draw any pixels. */ - virtual bool isInvisible() const throw() = 0; - - virtual void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw() = 0; - - virtual void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw() = 0; - - virtual void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw() = 0; - - virtual void paintVerticalLine (LowLevelGraphicsContext& context, - int x, float y1, float y2) throw(); - - virtual void paintHorizontalLine (LowLevelGraphicsContext& context, - int y, float x1, float x2) throw(); - - virtual void paintLine (LowLevelGraphicsContext& context, - float x1, float y1, float x2, float y2) throw(); - -private: - - Brush (const Brush&); - const Brush& operator= (const Brush&); -}; - -#endif // __JUCE_BRUSH_JUCEHEADER__ -/********* End of inlined file: juce_Brush.h *********/ - -/** - A Brush that fills its area with a solid (or semi-transparent) colour. - - An application won't normally need to use this class directly, as drawing - with solid colours is taken care of automatically by the Graphics class - (it actually uses one of these brushes internally when you set the colour - with the Graphics::setColour() method). - - @see Brush, Graphics::setBrush, GradientBrush, ImageBrush -*/ -class JUCE_API SolidColourBrush : public Brush -{ -public: - - /** Creates a SolidColourBrush to draw with the given colour. - - The colour can be changed later with the setColour() method. - */ - SolidColourBrush (const Colour& colour) throw(); - - /** Creates a SolidColourBrush set to black. - - The colour can be changed later with the setColour() method. - */ - SolidColourBrush() throw(); - - /** Destructor. */ - ~SolidColourBrush() throw(); - - /** Returns the colour currently being used. */ - const Colour& getColour() const throw() { return colour; } - - /** Sets the colour to use for drawing. */ - void setColour (const Colour& newColour) throw() { colour = newColour; } - - Brush* createCopy() const throw(); - - void applyTransform (const AffineTransform& transform) throw(); - - bool isInvisible() const throw(); - - void multiplyOpacity (const float multiple) throw(); - - void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw(); - - void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw(); - - void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw(); - - void paintVerticalLine (LowLevelGraphicsContext& context, - int x, float y1, float y2) throw(); - - void paintHorizontalLine (LowLevelGraphicsContext& context, - int y, float x1, float x2) throw(); - - void paintLine (LowLevelGraphicsContext& context, - float x1, float y1, float x2, float y2) throw(); - - juce_UseDebuggingNewOperator - -private: - Colour colour; - - SolidColourBrush (const SolidColourBrush&); - const SolidColourBrush& operator= (const SolidColourBrush&); -}; - -#endif // __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ -/********* End of inlined file: juce_SolidColourBrush.h *********/ - /********* Start of inlined file: juce_RectanglePlacement.h *********/ #ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ #define __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ @@ -20173,7 +19999,7 @@ public: 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, setBrush + @see setOpacity */ void setColour (const Colour& newColour) throw(); @@ -20188,18 +20014,6 @@ public: */ void setOpacity (const float newOpacity) throw(); - /** Changes the current brush to use for drawing. - - If a null pointer is passed in, the context will revert to using a solid - colour for drawing (using the last colour set by setColour()). - - If a brush is passed in, a copy of it will be used for subsequent drawing - operations until setColour() or setBrush() is called. - - @see SolidColourBrush, GradientBrush, ImageBrush, Brush - */ - void setBrush (const Brush* const newBrush) throw(); - /** Sets the context to use a gradient for its fill pattern. */ void setGradientFill (const ColourGradient& gradient) throw(); @@ -20208,7 +20022,7 @@ public: Make sure that you don't delete this image while it's still being used by this context! */ - void setTiledImageFill (Image& imageToUse, + void setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity) throw(); @@ -20780,12 +20594,13 @@ public: /** @internal */ LowLevelGraphicsContext* getInternalContext() const throw() { return context; } - /*class FillType + class FillType { public: + FillType() throw(); FillType (const Colour& colour) throw(); FillType (const ColourGradient& gradient) throw(); - FillType (Image* image, int x, int y) throw(); + FillType (Image* const image, const int x, const int y) throw(); FillType (const FillType& other) throw(); const FillType& operator= (const FillType& other) throw(); ~FillType() throw(); @@ -20796,35 +20611,22 @@ public: void setColour (const Colour& newColour) throw(); void setGradient (const ColourGradient& newGradient) throw(); - void setTiledImage (Image* image, const int imageX, const int imageY) throw(); + void setTiledImage (const Image& image, const int imageX, const int imageY) throw(); Colour colour; ColourGradient* gradient; - Image* image; + const Image* image; int imageX, imageY; juce_UseDebuggingNewOperator - };*/ + }; private: LowLevelGraphicsContext* const context; const bool ownsContext; - struct GraphicsState - { - GraphicsState() throw(); - GraphicsState (const GraphicsState&) throw(); - ~GraphicsState() throw(); - - Brush* brush; - Font font; - }; - - GraphicsState* state; - OwnedArray stateStack; bool saveStatePending; - void saveStateIfPending() throw(); const Graphics& operator= (const Graphics& other); @@ -37404,248 +37206,6 @@ public: #endif // __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ /********* End of inlined file: juce_AiffAudioFormat.h *********/ -#endif -#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ - -/********* Start of inlined file: juce_AudioCDBurner.h *********/ -#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ -#define __JUCE_AUDIOCDBURNER_JUCEHEADER__ - -#if JUCE_USE_CDBURNER - -/** -*/ -class AudioCDBurner -{ -public: - - /** Returns a list of available optical drives. - - Use openDevice() to open one of the items from this list. - */ - static const StringArray findAvailableDevices(); - - /** Tries to open one of the optical drives. - - The deviceIndex is an index into the array returned by findAvailableDevices(). - */ - static AudioCDBurner* openDevice (const int deviceIndex); - - /** Destructor. */ - ~AudioCDBurner(); - - /** Returns true if there's a writable disk in the drive. - */ - bool isDiskPresent() const; - - /** Returns the number of free blocks on the disk. - - There are 75 blocks per second, at 44100Hz. - */ - int getNumAvailableAudioBlocks() const; - - /** Adds a track to be written. - - The source passed-in here will be kept by this object, and it will - be used and deleted at some point in the future, either during the - burn() method or when this AudioCDBurner object is deleted. Your caller - method shouldn't keep a reference to it or use it again after passing - it in here. - */ - bool addAudioTrack (AudioSource* source, int numSamples); - - /** - - Return true to cancel the current burn operation - */ - class BurnProgressListener - { - public: - BurnProgressListener() throw() {} - virtual ~BurnProgressListener() {} - - /** Called at intervals to report on the progress of the AudioCDBurner. - - To cancel the burn, return true from this. - */ - virtual bool audioCDBurnProgress (float proportionComplete) = 0; - }; - - const String burn (BurnProgressListener* listener, - const bool ejectDiscAfterwards, - const bool peformFakeBurnForTesting); - - juce_UseDebuggingNewOperator - -private: - AudioCDBurner (const int deviceIndex); - - void* internal; -}; - -#endif -#endif // __JUCE_AUDIOCDBURNER_JUCEHEADER__ -/********* End of inlined file: juce_AudioCDBurner.h *********/ - -#endif -#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ - -/********* Start of inlined file: juce_AudioCDReader.h *********/ -#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ -#define __JUCE_AUDIOCDREADER_JUCEHEADER__ - -#if JUCE_USE_CDREADER - -#if JUCE_MAC - -#endif - -/** - A type of AudioFormatReader that reads from an audio CD. - - One of these can be used to read a CD as if it's one big audio stream. Use the - getPositionOfTrackStart() method to find where the individual tracks are - within the stream. - - @see AudioFormatReader -*/ -class JUCE_API AudioCDReader : public AudioFormatReader -{ -public: - - /** Returns a list of names of Audio CDs currently available for reading. - - If there's a CD drive but no CD in it, this might return an empty list, or - possibly a device that can be opened but which has no tracks, depending - on the platform. - - @see createReaderForCD - */ - static const StringArray getAvailableCDNames(); - - /** Tries to create an AudioFormatReader that can read from an Audio CD. - - @param index the index of one of the available CDs - use getAvailableCDNames() - to find out how many there are. - @returns a new AudioCDReader object, or 0 if it couldn't be created. The - caller will be responsible for deleting the object returned. - */ - static AudioCDReader* createReaderForCD (const int index); - - /** Destructor. */ - ~AudioCDReader(); - - /** Implementation of the AudioFormatReader method. */ - bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples); - - /** Checks whether the CD has been removed from the drive. - */ - bool isCDStillPresent() const; - - /** Returns the total number of tracks (audio + data). - */ - int getNumTracks() const; - - /** Finds the sample offset of the start of a track. - - @param trackNum the track number, where 0 is the first track. - */ - int getPositionOfTrackStart (int trackNum) const; - - /** Returns true if a given track is an audio track. - - @param trackNum the track number, where 0 is the first track. - */ - bool isTrackAudio (int trackNum) const; - - /** Refreshes the object's table of contents. - - If the disc has been ejected and a different one put in since this - object was created, this will cause it to update its idea of how many tracks - there are, etc. - */ - void refreshTrackLengths(); - - /** Enables scanning for indexes within tracks. - - @see getLastIndex - */ - void enableIndexScanning (bool enabled); - - /** Returns the index number found during the last read() call. - - Index scanning is turned off by default - turn it on with enableIndexScanning(). - - Then when the read() method is called, if it comes across an index within that - block, the index number is stored and returned by this method. - - Some devices might not support indexes, of course. - - (If you don't know what CD indexes are, it's unlikely you'll ever need them). - - @see enableIndexScanning - */ - int getLastIndex() const; - - /** Scans a track to find the position of any indexes within it. - - @param trackNumber the track to look in, where 0 is the first track on the disc - @returns an array of sample positions of any index points found (not including - the index that marks the start of the track) - */ - const Array findIndexesInTrack (const int trackNumber); - - /** Returns the CDDB id number for the CD. - - It's not a great way of identifying a disc, but it's traditional. - */ - int getCDDBId(); - - /** Tries to eject the disk. - - Of course this might not be possible, if some other process is using it. - */ - void ejectDisk(); - - juce_UseDebuggingNewOperator - -private: - -#if JUCE_MAC - File volumeDir; - OwnedArray tracks; - Array trackStartSamples; - int currentReaderTrack; - AudioFormatReader* reader; - AudioCDReader (const File& volume); -public: - static int compareElements (const File* const, const File* const) throw(); -private: - -#elif JUCE_WINDOWS - int numTracks; - int trackStarts[100]; - bool audioTracks [100]; - void* handle; - bool indexingEnabled; - int lastIndex, firstFrameInBuffer, samplesInBuffer; - MemoryBlock buffer; - AudioCDReader (void* handle); - int getIndexAt (int samplePos); - -#elif JUCE_LINUX - AudioCDReader(); -#endif - - AudioCDReader (const AudioCDReader&); - const AudioCDReader& operator= (const AudioCDReader&); -}; - -#endif -#endif // __JUCE_AUDIOCDREADER_JUCEHEADER__ -/********* End of inlined file: juce_AudioCDReader.h *********/ - #endif #ifndef __JUCE_AUDIOFORMAT_JUCEHEADER__ @@ -38107,114 +37667,6 @@ public: #endif // __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ /********* End of inlined file: juce_FlacAudioFormat.h *********/ -#endif -#ifndef __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ - -/********* Start of inlined file: juce_OggVorbisAudioFormat.h *********/ -#ifndef __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ -#define __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ - -#if JUCE_USE_OGGVORBIS || defined (DOXYGEN) - -/** - Reads and writes the Ogg-Vorbis audio format. - - To compile this, you'll need to set the JUCE_USE_OGGVORBIS flag in juce_Config.h, - and make sure your include search path and library search path are set up to find - the Vorbis and Ogg header files and static libraries. - - @see AudioFormat, -*/ -class JUCE_API OggVorbisAudioFormat : public AudioFormat -{ -public: - - OggVorbisAudioFormat(); - ~OggVorbisAudioFormat(); - - const Array getPossibleSampleRates(); - const Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); - bool isCompressed(); - const StringArray getQualityOptions(); - - /** Tries to estimate the quality level of an ogg file based on its size. - - If it can't read the file for some reason, this will just return 1 (medium quality), - otherwise it will return the approximate quality setting that would have been used - to create the file. - - @see getQualityOptions - */ - int estimateOggFileQuality (const File& source); - - AudioFormatReader* createReaderFor (InputStream* sourceStream, - const bool deleteStreamIfOpeningFails); - - AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, - double sampleRateToUse, - unsigned int numberOfChannels, - int bitsPerSample, - const StringPairArray& metadataValues, - int qualityOptionIndex); - - juce_UseDebuggingNewOperator -}; - -#endif -#endif // __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ -/********* End of inlined file: juce_OggVorbisAudioFormat.h *********/ - -#endif -#ifndef __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ - -/********* Start of inlined file: juce_QuickTimeAudioFormat.h *********/ -#ifndef __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ -#define __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ - -#if JUCE_QUICKTIME - -/** - Uses QuickTime to read the audio track a movie or media file. - - As well as QuickTime movies, this should also manage to open other audio - files that quicktime can understand, like mp3, m4a, etc. - - @see AudioFormat -*/ -class JUCE_API QuickTimeAudioFormat : public AudioFormat -{ -public: - - /** Creates a format object. */ - QuickTimeAudioFormat(); - - /** Destructor. */ - ~QuickTimeAudioFormat(); - - const Array getPossibleSampleRates(); - const Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); - - AudioFormatReader* createReaderFor (InputStream* sourceStream, - const bool deleteStreamIfOpeningFails); - - AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, - double sampleRateToUse, - unsigned int numberOfChannels, - int bitsPerSample, - const StringPairArray& metadataValues, - int qualityOptionIndex); - - juce_UseDebuggingNewOperator -}; - -#endif -#endif // __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ -/********* End of inlined file: juce_QuickTimeAudioFormat.h *********/ - #endif #ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ @@ -38337,6 +37789,356 @@ public: #endif // __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ /********* End of inlined file: juce_WavAudioFormat.h *********/ +#endif +#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioCDReader.h *********/ +#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ +#define __JUCE_AUDIOCDREADER_JUCEHEADER__ + +#if JUCE_USE_CDREADER + +#if JUCE_MAC + +#endif + +/** + A type of AudioFormatReader that reads from an audio CD. + + One of these can be used to read a CD as if it's one big audio stream. Use the + getPositionOfTrackStart() method to find where the individual tracks are + within the stream. + + @see AudioFormatReader +*/ +class JUCE_API AudioCDReader : public AudioFormatReader +{ +public: + + /** Returns a list of names of Audio CDs currently available for reading. + + If there's a CD drive but no CD in it, this might return an empty list, or + possibly a device that can be opened but which has no tracks, depending + on the platform. + + @see createReaderForCD + */ + static const StringArray getAvailableCDNames(); + + /** Tries to create an AudioFormatReader that can read from an Audio CD. + + @param index the index of one of the available CDs - use getAvailableCDNames() + to find out how many there are. + @returns a new AudioCDReader object, or 0 if it couldn't be created. The + caller will be responsible for deleting the object returned. + */ + static AudioCDReader* createReaderForCD (const int index); + + /** Destructor. */ + ~AudioCDReader(); + + /** Implementation of the AudioFormatReader method. */ + bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples); + + /** Checks whether the CD has been removed from the drive. + */ + bool isCDStillPresent() const; + + /** Returns the total number of tracks (audio + data). + */ + int getNumTracks() const; + + /** Finds the sample offset of the start of a track. + + @param trackNum the track number, where 0 is the first track. + */ + int getPositionOfTrackStart (int trackNum) const; + + /** Returns true if a given track is an audio track. + + @param trackNum the track number, where 0 is the first track. + */ + bool isTrackAudio (int trackNum) const; + + /** Refreshes the object's table of contents. + + If the disc has been ejected and a different one put in since this + object was created, this will cause it to update its idea of how many tracks + there are, etc. + */ + void refreshTrackLengths(); + + /** Enables scanning for indexes within tracks. + + @see getLastIndex + */ + void enableIndexScanning (bool enabled); + + /** Returns the index number found during the last read() call. + + Index scanning is turned off by default - turn it on with enableIndexScanning(). + + Then when the read() method is called, if it comes across an index within that + block, the index number is stored and returned by this method. + + Some devices might not support indexes, of course. + + (If you don't know what CD indexes are, it's unlikely you'll ever need them). + + @see enableIndexScanning + */ + int getLastIndex() const; + + /** Scans a track to find the position of any indexes within it. + + @param trackNumber the track to look in, where 0 is the first track on the disc + @returns an array of sample positions of any index points found (not including + the index that marks the start of the track) + */ + const Array findIndexesInTrack (const int trackNumber); + + /** Returns the CDDB id number for the CD. + + It's not a great way of identifying a disc, but it's traditional. + */ + int getCDDBId(); + + /** Tries to eject the disk. + + Of course this might not be possible, if some other process is using it. + */ + void ejectDisk(); + + juce_UseDebuggingNewOperator + +private: + +#if JUCE_MAC + File volumeDir; + OwnedArray tracks; + Array trackStartSamples; + int currentReaderTrack; + AudioFormatReader* reader; + AudioCDReader (const File& volume); +public: + static int compareElements (const File* const, const File* const) throw(); +private: + +#elif JUCE_WINDOWS + int numTracks; + int trackStarts[100]; + bool audioTracks [100]; + void* handle; + bool indexingEnabled; + int lastIndex, firstFrameInBuffer, samplesInBuffer; + MemoryBlock buffer; + AudioCDReader (void* handle); + int getIndexAt (int samplePos); + +#elif JUCE_LINUX + AudioCDReader(); +#endif + + AudioCDReader (const AudioCDReader&); + const AudioCDReader& operator= (const AudioCDReader&); +}; + +#endif +#endif // __JUCE_AUDIOCDREADER_JUCEHEADER__ +/********* End of inlined file: juce_AudioCDReader.h *********/ + +#endif +#ifndef __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_OggVorbisAudioFormat.h *********/ +#ifndef __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ +#define __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ + +#if JUCE_USE_OGGVORBIS || defined (DOXYGEN) + +/** + Reads and writes the Ogg-Vorbis audio format. + + To compile this, you'll need to set the JUCE_USE_OGGVORBIS flag in juce_Config.h, + and make sure your include search path and library search path are set up to find + the Vorbis and Ogg header files and static libraries. + + @see AudioFormat, +*/ +class JUCE_API OggVorbisAudioFormat : public AudioFormat +{ +public: + + OggVorbisAudioFormat(); + ~OggVorbisAudioFormat(); + + const Array getPossibleSampleRates(); + const Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); + bool isCompressed(); + const StringArray getQualityOptions(); + + /** Tries to estimate the quality level of an ogg file based on its size. + + If it can't read the file for some reason, this will just return 1 (medium quality), + otherwise it will return the approximate quality setting that would have been used + to create the file. + + @see getQualityOptions + */ + int estimateOggFileQuality (const File& source); + + AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails); + + AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex); + + juce_UseDebuggingNewOperator +}; + +#endif +#endif // __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_OggVorbisAudioFormat.h *********/ + +#endif +#ifndef __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_QuickTimeAudioFormat.h *********/ +#ifndef __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ +#define __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ + +#if JUCE_QUICKTIME + +/** + Uses QuickTime to read the audio track a movie or media file. + + As well as QuickTime movies, this should also manage to open other audio + files that quicktime can understand, like mp3, m4a, etc. + + @see AudioFormat +*/ +class JUCE_API QuickTimeAudioFormat : public AudioFormat +{ +public: + + /** Creates a format object. */ + QuickTimeAudioFormat(); + + /** Destructor. */ + ~QuickTimeAudioFormat(); + + const Array getPossibleSampleRates(); + const Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); + + AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails); + + AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex); + + juce_UseDebuggingNewOperator +}; + +#endif +#endif // __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_QuickTimeAudioFormat.h *********/ + +#endif +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ + +/********* Start of inlined file: juce_AudioCDBurner.h *********/ +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ +#define __JUCE_AUDIOCDBURNER_JUCEHEADER__ + +#if JUCE_USE_CDBURNER + +/** +*/ +class AudioCDBurner +{ +public: + + /** Returns a list of available optical drives. + + Use openDevice() to open one of the items from this list. + */ + static const StringArray findAvailableDevices(); + + /** Tries to open one of the optical drives. + + The deviceIndex is an index into the array returned by findAvailableDevices(). + */ + static AudioCDBurner* openDevice (const int deviceIndex); + + /** Destructor. */ + ~AudioCDBurner(); + + /** Returns true if there's a writable disk in the drive. + */ + bool isDiskPresent() const; + + /** Returns the number of free blocks on the disk. + + There are 75 blocks per second, at 44100Hz. + */ + int getNumAvailableAudioBlocks() const; + + /** Adds a track to be written. + + The source passed-in here will be kept by this object, and it will + be used and deleted at some point in the future, either during the + burn() method or when this AudioCDBurner object is deleted. Your caller + method shouldn't keep a reference to it or use it again after passing + it in here. + */ + bool addAudioTrack (AudioSource* source, int numSamples); + + /** + + Return true to cancel the current burn operation + */ + class BurnProgressListener + { + public: + BurnProgressListener() throw() {} + virtual ~BurnProgressListener() {} + + /** Called at intervals to report on the progress of the AudioCDBurner. + + To cancel the burn, return true from this. + */ + virtual bool audioCDBurnProgress (float proportionComplete) = 0; + }; + + const String burn (BurnProgressListener* listener, + const bool ejectDiscAfterwards, + const bool peformFakeBurnForTesting); + + juce_UseDebuggingNewOperator + +private: + AudioCDBurner (const int deviceIndex); + + void* internal; +}; + +#endif +#endif // __JUCE_AUDIOCDBURNER_JUCEHEADER__ +/********* End of inlined file: juce_AudioCDBurner.h *********/ + #endif #ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__ @@ -39172,435 +38974,6 @@ private: #endif #ifndef __JUCE_TIMER_JUCEHEADER__ -#endif -#ifndef __JUCE_BRUSH_JUCEHEADER__ - -#endif -#ifndef __JUCE_GRADIENTBRUSH_JUCEHEADER__ - -/********* Start of inlined file: juce_GradientBrush.h *********/ -#ifndef __JUCE_GRADIENTBRUSH_JUCEHEADER__ -#define __JUCE_GRADIENTBRUSH_JUCEHEADER__ - -/** - A Brush that fills areas with a colour gradient. - - The gradient can either be linear or circular. - - @see Brush, Graphics::setBrush, SolidColourBrush, ImageBrush -*/ -class JUCE_API GradientBrush : public Brush -{ -public: - - /** Creates a gradient brush, ready for use in Graphics::setBrush(). - - (x1, y1) is the location relative to the origin of the Graphics context, - at which the colour should be colour1. Likewise for (x2, y2) and colour2. - - 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 the brush - need not be completely opaque. Note that this means 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 - grey colour, but Colour::transparentWhite to Colours::white will be - white all the way across. - - @see ColourGradient - */ - GradientBrush (const Colour& colour1, - const float x1, - const float y1, - const Colour& colour2, - const float x2, - const float y2, - const bool isRadial) throw(); - - /** Creates a gradient brush from a ColourGradient object. - */ - GradientBrush (const ColourGradient& gradient) throw(); - - /** Destructor. */ - ~GradientBrush() throw(); - - /** Returns the current gradient information */ - const ColourGradient& getGradient() const throw() { return gradient; } - - Brush* createCopy() const throw(); - - void applyTransform (const AffineTransform& transform) throw(); - - void multiplyOpacity (const float multiple) throw(); - - bool isInvisible() const throw(); - - void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw(); - - void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw(); - - void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw(); - - juce_UseDebuggingNewOperator - -protected: - ColourGradient gradient; - -private: - GradientBrush (const GradientBrush&); - const GradientBrush& operator= (const GradientBrush&); -}; - -#endif // __JUCE_GRADIENTBRUSH_JUCEHEADER__ -/********* End of inlined file: juce_GradientBrush.h *********/ - -#endif -#ifndef __JUCE_IMAGEBRUSH_JUCEHEADER__ - -/********* Start of inlined file: juce_ImageBrush.h *********/ -#ifndef __JUCE_IMAGEBRUSH_JUCEHEADER__ -#define __JUCE_IMAGEBRUSH_JUCEHEADER__ - -/********* Start of inlined file: juce_Image.h *********/ -#ifndef __JUCE_IMAGE_JUCEHEADER__ -#define __JUCE_IMAGE_JUCEHEADER__ - -/** - Holds a fixed-size bitmap. - - The image is stored in either 24-bit RGB or 32-bit premultiplied-ARGB format. - - To draw into an image, create a Graphics object for it. - e.g. @code - - // create a transparent 500x500 image.. - Image myImage (Image::RGB, 500, 500, true); - - Graphics g (myImage); - g.setColour (Colours::red); - g.fillEllipse (20, 20, 300, 200); // draws a red ellipse in our image. - @endcode - - Other useful ways to create an image are with the ImageCache class, or the - ImageFileFormat, which provides a way to load common image files. - - @see Graphics, ImageFileFormat, ImageCache, ImageConvolutionKernel -*/ -class JUCE_API Image -{ -public: - - enum PixelFormat - { - RGB, /**<< each pixel is a 3-byte packed RGB colour value. For byte order, see the PixelRGB class. */ - ARGB, /**<< each pixel is a 4-byte ARGB premultiplied colour value. For byte order, see the PixelARGB class. */ - SingleChannel /**<< each pixel is a 1-byte alpha channel value. */ - }; - - /** Creates an in-memory image with a specified size and format. - - To create an image that can use native OS rendering methods, see createNativeImage(). - - @param format the number of colour channels in the image - @param imageWidth the desired width of the image, in pixels - this value must be - greater than zero (otherwise a width of 1 will be used) - @param imageHeight the desired width of the image, in pixels - this value must be - greater than zero (otherwise a height of 1 will be used) - @param clearImage if true, the image will initially be cleared to black or transparent - black. If false, the image may contain random data, and the - user will have to deal with this - */ - Image (const PixelFormat format, - const int imageWidth, - const int imageHeight, - const bool clearImage); - - /** Creates a copy of another image. - - @see createCopy - */ - Image (const Image& other); - - /** Destructor. */ - virtual ~Image(); - - /** Tries to create an image that is uses native drawing methods when you render - onto it. - - On some platforms this will just return a normal software-based image. - */ - static Image* createNativeImage (const PixelFormat format, - const int imageWidth, - const int imageHeight, - const bool clearImage); - - /** Returns the image's width (in pixels). */ - int getWidth() const throw() { return imageWidth; } - - /** Returns the image's height (in pixels). */ - int getHeight() const throw() { return imageHeight; } - - /** Returns the image's pixel format. */ - PixelFormat getFormat() const throw() { return format; } - - /** True if the image's format is ARGB. */ - bool isARGB() const throw() { return format == ARGB; } - - /** True if the image's format is RGB. */ - bool isRGB() const throw() { return format == RGB; } - - /** True if the image contains an alpha-channel. */ - bool hasAlphaChannel() const throw() { return format != RGB; } - - /** Clears a section of the image with a given colour. - - This won't do any alpha-blending - it just sets all pixels in the image to - the given colour (which may be non-opaque if the image has an alpha channel). - */ - virtual void clear (int x, int y, int w, int h, - const Colour& colourToClearTo = Colour (0x00000000)); - - /** Returns a new image that's a copy of this one. - - A new size for the copied image can be specified, or values less than - zero can be passed-in to use the image's existing dimensions. - - It's up to the caller to delete the image when no longer needed. - */ - virtual Image* createCopy (int newWidth = -1, - int newHeight = -1, - const Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const; - - /** Returns a new single-channel image which is a copy of the alpha-channel of this image. - */ - virtual Image* createCopyOfAlphaChannel() const; - - /** Returns the colour of one of the pixels in the image. - - If the co-ordinates given are beyond the image's boundaries, this will - return Colours::transparentBlack. - - (0, 0) is the image's top-left corner. - - @see getAlphaAt, setPixelAt, blendPixelAt - */ - virtual const Colour getPixelAt (const int x, const int y) const; - - /** Sets the colour of one of the image's pixels. - - If the co-ordinates are beyond the image's boundaries, then nothing will - happen. - - Note that unlike blendPixelAt(), this won't do any alpha-blending, it'll - just replace the existing pixel with the given one. The colour's opacity - will be ignored if this image doesn't have an alpha-channel. - - (0, 0) is the image's top-left corner. - - @see blendPixelAt - */ - virtual void setPixelAt (const int x, const int y, const Colour& colour); - - /** Changes the opacity of a pixel. - - This only has an effect if the image has an alpha channel and if the - given co-ordinates are inside the image's boundary. - - The multiplier must be in the range 0 to 1.0, and the current alpha - at the given co-ordinates will be multiplied by this value. - - @see getAlphaAt, setPixelAt - */ - virtual void multiplyAlphaAt (const int x, const int y, const float multiplier); - - /** Changes the overall opacity of the image. - - This will multiply the alpha value of each pixel in the image by the given - amount (limiting the resulting alpha values between 0 and 255). This allows - you to make an image more or less transparent. - - If the image doesn't have an alpha channel, this won't have any effect. - */ - virtual void multiplyAllAlphas (const float amountToMultiplyBy); - - /** Changes all the colours to be shades of grey, based on their current luminosity. - */ - virtual void desaturate(); - - /** Retrieves a section of an image as raw pixel data, so it can be read or written to. - - You should only use this class as a last resort - messing about with the internals of - an image is only recommended for people who really know what they're doing! - - A BitmapData object should be used as a temporary, stack-based object. Don't keep one - hanging around while the image is being used elsewhere. - - Depending on the way the image class is implemented, this may create a temporary buffer - which is copied back to the image when the object is deleted, or it may just get a pointer - directly into the image's raw data. - - You can use the stride and data values in this class directly, but don't alter them! - The actual format of the pixel data depends on the image's format - see Image::getFormat(), - and the PixelRGB, PixelARGB and PixelAlpha classes for more info. - */ - class BitmapData - { - public: - BitmapData (Image& image, int x, int y, int w, int h, const bool needsToBeWritable) throw(); - BitmapData (const Image& image, int x, int y, int w, int h) throw(); - ~BitmapData() throw(); - - /** Returns a pointer to the start of a line in the image. - The co-ordinate you provide here isn't checked, so it's the caller's responsibility to make - sure it's not out-of-range. - */ - inline uint8* getLinePointer (const int y) const throw() { return data + y * lineStride; } - - /** Returns a pointer to a pixel in the image. - The co-ordinates you give here are not checked, so it's the caller's responsibility to make sure they're - not out-of-range. - */ - inline uint8* getPixelPointer (const int x, const int y) const throw() { return data + y * lineStride + x * pixelStride; } - - uint8* data; - int lineStride, pixelStride, width, height; - }; - - /** Copies some pixel values to a rectangle of the image. - - The format of the pixel data must match that of the image itself, and the - rectangle supplied must be within the image's bounds. - */ - virtual void setPixelData (int destX, int destY, int destW, int destH, - const uint8* sourcePixelData, int sourceLineStride); - - /** Copies a section of the image to somewhere else within itself. - */ - virtual void moveImageSection (int destX, int destY, - int sourceX, int sourceY, - int width, int height); - - /** Creates a RectangleList containing rectangles for all non-transparent pixels - of the image. - - @param result the list that will have the area added to it - @param alphaThreshold for a semi-transparent image, any pixels whose alpha is - above this level will be considered opaque - */ - void createSolidAreaMask (RectangleList& result, - const float alphaThreshold = 0.5f) const; - - juce_UseDebuggingNewOperator - - /** Creates a context suitable for drawing onto this image. - - Don't call this method directly! It's used internally by the Graphics class. - */ - virtual LowLevelGraphicsContext* createLowLevelContext(); - -protected: - friend class BitmapData; - const PixelFormat format; - const int imageWidth, imageHeight; - - /** Used internally so that subclasses can call a constructor that doesn't allocate memory */ - Image (const PixelFormat format, - const int imageWidth, - const int imageHeight); - - int pixelStride, lineStride; - uint8* imageData; - -private: - - const Image& operator= (const Image&); -}; - -#endif // __JUCE_IMAGE_JUCEHEADER__ -/********* End of inlined file: juce_Image.h *********/ - -/** - A Brush that fills areas with tiled repetitions of an image. - - @see Brush, Graphics::setBrush, SolidColourBrush, GradientBrush -*/ -class JUCE_API ImageBrush : public Brush -{ -public: - - /* Creates an image brush, ready for use in Graphics::setBrush(). - - (x, y) is an anchor point for the top-left of the image - A reference to the image passed in will be kept, so don't delete - it within the lifetime of this object - */ - ImageBrush (Image* const image, - const int anchorX, - const int anchorY, - const float opacity) throw(); - - /** Destructor. */ - ~ImageBrush() throw(); - - /** Returns the image currently being used. */ - Image* getImage() const throw() { return image; } - - /** Returns the current anchor X position. */ - int getAnchorX() const throw() { return anchorX; } - - /** Returns the current anchor Y position. */ - int getAnchorY() const throw() { return anchorY; } - - /** Returns the current opacity. */ - float getOpacity() const throw() { return opacity; } - - Brush* createCopy() const throw(); - - void applyTransform (const AffineTransform& transform) throw(); - - void multiplyOpacity (const float multiple) throw(); - - bool isInvisible() const throw(); - - void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw(); - - void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw(); - - void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw(); - - juce_UseDebuggingNewOperator - -protected: - Image* image; - int anchorX, anchorY; - float opacity; - -private: - ImageBrush (const ImageBrush&); - const ImageBrush& operator= (const ImageBrush&); - - void getStartXY (int& x, int& y) const throw(); -}; - -#endif // __JUCE_IMAGEBRUSH_JUCEHEADER__ -/********* End of inlined file: juce_ImageBrush.h *********/ - -#endif -#ifndef __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ - -#endif -#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ - #endif #ifndef __JUCE_COLOUR_JUCEHEADER__ @@ -39611,7 +38984,10 @@ private: #ifndef __JUCE_COLOURGRADIENT_JUCEHEADER__ #endif -#ifndef __JUCE_FONT_JUCEHEADER__ +#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ + +#endif +#ifndef __JUCE_TYPEFACE_JUCEHEADER__ #endif #ifndef __JUCE_TEXTLAYOUT_JUCEHEADER__ @@ -39733,7 +39109,7 @@ private: /********* End of inlined file: juce_TextLayout.h *********/ #endif -#ifndef __JUCE_TYPEFACE_JUCEHEADER__ +#ifndef __JUCE_FONT_JUCEHEADER__ #endif #ifndef __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ @@ -40020,11 +39396,17 @@ private: /********* End of inlined file: juce_GlyphArrangement.h *********/ #endif -#ifndef __JUCE_EDGETABLE_JUCEHEADER__ +#ifndef __JUCE_GRAPHICS_JUCEHEADER__ #endif #ifndef __JUCE_JUSTIFICATION_JUCEHEADER__ +#endif +#ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ + +#endif +#ifndef __JUCE_EDGETABLE_JUCEHEADER__ + #endif #ifndef __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ @@ -40032,6 +39414,264 @@ private: #ifndef __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ #define __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ +/********* Start of inlined file: juce_Image.h *********/ +#ifndef __JUCE_IMAGE_JUCEHEADER__ +#define __JUCE_IMAGE_JUCEHEADER__ + +/** + Holds a fixed-size bitmap. + + The image is stored in either 24-bit RGB or 32-bit premultiplied-ARGB format. + + To draw into an image, create a Graphics object for it. + e.g. @code + + // create a transparent 500x500 image.. + Image myImage (Image::RGB, 500, 500, true); + + Graphics g (myImage); + g.setColour (Colours::red); + g.fillEllipse (20, 20, 300, 200); // draws a red ellipse in our image. + @endcode + + Other useful ways to create an image are with the ImageCache class, or the + ImageFileFormat, which provides a way to load common image files. + + @see Graphics, ImageFileFormat, ImageCache, ImageConvolutionKernel +*/ +class JUCE_API Image +{ +public: + + enum PixelFormat + { + RGB, /**<< each pixel is a 3-byte packed RGB colour value. For byte order, see the PixelRGB class. */ + ARGB, /**<< each pixel is a 4-byte ARGB premultiplied colour value. For byte order, see the PixelARGB class. */ + SingleChannel /**<< each pixel is a 1-byte alpha channel value. */ + }; + + /** Creates an in-memory image with a specified size and format. + + To create an image that can use native OS rendering methods, see createNativeImage(). + + @param format the number of colour channels in the image + @param imageWidth the desired width of the image, in pixels - this value must be + greater than zero (otherwise a width of 1 will be used) + @param imageHeight the desired width of the image, in pixels - this value must be + greater than zero (otherwise a height of 1 will be used) + @param clearImage if true, the image will initially be cleared to black or transparent + black. If false, the image may contain random data, and the + user will have to deal with this + */ + Image (const PixelFormat format, + const int imageWidth, + const int imageHeight, + const bool clearImage); + + /** Creates a copy of another image. + + @see createCopy + */ + Image (const Image& other); + + /** Destructor. */ + virtual ~Image(); + + /** Tries to create an image that is uses native drawing methods when you render + onto it. + + On some platforms this will just return a normal software-based image. + */ + static Image* createNativeImage (const PixelFormat format, + const int imageWidth, + const int imageHeight, + const bool clearImage); + + /** Returns the image's width (in pixels). */ + int getWidth() const throw() { return imageWidth; } + + /** Returns the image's height (in pixels). */ + int getHeight() const throw() { return imageHeight; } + + /** Returns the image's pixel format. */ + PixelFormat getFormat() const throw() { return format; } + + /** True if the image's format is ARGB. */ + bool isARGB() const throw() { return format == ARGB; } + + /** True if the image's format is RGB. */ + bool isRGB() const throw() { return format == RGB; } + + /** True if the image contains an alpha-channel. */ + bool hasAlphaChannel() const throw() { return format != RGB; } + + /** Clears a section of the image with a given colour. + + This won't do any alpha-blending - it just sets all pixels in the image to + the given colour (which may be non-opaque if the image has an alpha channel). + */ + virtual void clear (int x, int y, int w, int h, + const Colour& colourToClearTo = Colour (0x00000000)); + + /** Returns a new image that's a copy of this one. + + A new size for the copied image can be specified, or values less than + zero can be passed-in to use the image's existing dimensions. + + It's up to the caller to delete the image when no longer needed. + */ + virtual Image* createCopy (int newWidth = -1, + int newHeight = -1, + const Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const; + + /** Returns a new single-channel image which is a copy of the alpha-channel of this image. + */ + virtual Image* createCopyOfAlphaChannel() const; + + /** Returns the colour of one of the pixels in the image. + + If the co-ordinates given are beyond the image's boundaries, this will + return Colours::transparentBlack. + + (0, 0) is the image's top-left corner. + + @see getAlphaAt, setPixelAt, blendPixelAt + */ + virtual const Colour getPixelAt (const int x, const int y) const; + + /** Sets the colour of one of the image's pixels. + + If the co-ordinates are beyond the image's boundaries, then nothing will + happen. + + Note that unlike blendPixelAt(), this won't do any alpha-blending, it'll + just replace the existing pixel with the given one. The colour's opacity + will be ignored if this image doesn't have an alpha-channel. + + (0, 0) is the image's top-left corner. + + @see blendPixelAt + */ + virtual void setPixelAt (const int x, const int y, const Colour& colour); + + /** Changes the opacity of a pixel. + + This only has an effect if the image has an alpha channel and if the + given co-ordinates are inside the image's boundary. + + The multiplier must be in the range 0 to 1.0, and the current alpha + at the given co-ordinates will be multiplied by this value. + + @see getAlphaAt, setPixelAt + */ + virtual void multiplyAlphaAt (const int x, const int y, const float multiplier); + + /** Changes the overall opacity of the image. + + This will multiply the alpha value of each pixel in the image by the given + amount (limiting the resulting alpha values between 0 and 255). This allows + you to make an image more or less transparent. + + If the image doesn't have an alpha channel, this won't have any effect. + */ + virtual void multiplyAllAlphas (const float amountToMultiplyBy); + + /** Changes all the colours to be shades of grey, based on their current luminosity. + */ + virtual void desaturate(); + + /** Retrieves a section of an image as raw pixel data, so it can be read or written to. + + You should only use this class as a last resort - messing about with the internals of + an image is only recommended for people who really know what they're doing! + + A BitmapData object should be used as a temporary, stack-based object. Don't keep one + hanging around while the image is being used elsewhere. + + Depending on the way the image class is implemented, this may create a temporary buffer + which is copied back to the image when the object is deleted, or it may just get a pointer + directly into the image's raw data. + + You can use the stride and data values in this class directly, but don't alter them! + The actual format of the pixel data depends on the image's format - see Image::getFormat(), + and the PixelRGB, PixelARGB and PixelAlpha classes for more info. + */ + class BitmapData + { + public: + BitmapData (Image& image, int x, int y, int w, int h, const bool needsToBeWritable) throw(); + BitmapData (const Image& image, int x, int y, int w, int h) throw(); + ~BitmapData() throw(); + + /** Returns a pointer to the start of a line in the image. + The co-ordinate you provide here isn't checked, so it's the caller's responsibility to make + sure it's not out-of-range. + */ + inline uint8* getLinePointer (const int y) const throw() { return data + y * lineStride; } + + /** Returns a pointer to a pixel in the image. + The co-ordinates you give here are not checked, so it's the caller's responsibility to make sure they're + not out-of-range. + */ + inline uint8* getPixelPointer (const int x, const int y) const throw() { return data + y * lineStride + x * pixelStride; } + + uint8* data; + int lineStride, pixelStride, width, height; + }; + + /** Copies some pixel values to a rectangle of the image. + + The format of the pixel data must match that of the image itself, and the + rectangle supplied must be within the image's bounds. + */ + virtual void setPixelData (int destX, int destY, int destW, int destH, + const uint8* sourcePixelData, int sourceLineStride); + + /** Copies a section of the image to somewhere else within itself. + */ + virtual void moveImageSection (int destX, int destY, + int sourceX, int sourceY, + int width, int height); + + /** Creates a RectangleList containing rectangles for all non-transparent pixels + of the image. + + @param result the list that will have the area added to it + @param alphaThreshold for a semi-transparent image, any pixels whose alpha is + above this level will be considered opaque + */ + void createSolidAreaMask (RectangleList& result, + const float alphaThreshold = 0.5f) const; + + juce_UseDebuggingNewOperator + + /** Creates a context suitable for drawing onto this image. + + Don't call this method directly! It's used internally by the Graphics class. + */ + virtual LowLevelGraphicsContext* createLowLevelContext(); + +protected: + friend class BitmapData; + const PixelFormat format; + const int imageWidth, imageHeight; + + /** Used internally so that subclasses can call a constructor that doesn't allocate memory */ + Image (const PixelFormat format, + const int imageWidth, + const int imageHeight); + + int pixelStride, lineStride; + uint8* imageData; + +private: + + const Image& operator= (const Image&); +}; + +#endif // __JUCE_IMAGE_JUCEHEADER__ +/********* End of inlined file: juce_Image.h *********/ + /** Interface class for graphics context objects, used internally by the Graphics class. @@ -40064,53 +39704,38 @@ public: */ virtual void setOrigin (int x, int y) = 0; - /** Cliping co-ords are relative to the origin. */ - virtual bool reduceClipRegion (int x, int y, int w, int h) = 0; + virtual bool clipToRectangle (const Rectangle& r) = 0; + virtual bool clipToRectangleList (const RectangleList& clipRegion) = 0; + virtual void excludeClipRectangle (const Rectangle& r) = 0; + virtual void clipToPath (const Path& path, const AffineTransform& transform) = 0; + virtual void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) = 0; - /** Cliping co-ords are relative to the origin. */ - virtual bool reduceClipRegion (const RectangleList& clipRegion) = 0; - - //virtual bool clipToPath (const Path& path) = 0; - //virtual bool clipToImageAlpha (Image& image, int imageX, int imageY) = 0; - - /** Cliping co-ords are relative to the origin. */ - virtual void excludeClipRegion (int x, int y, int w, int h) = 0; + virtual bool clipRegionIntersects (const Rectangle& r) = 0; + virtual const Rectangle getClipBounds() const = 0; + virtual bool isClipEmpty() const = 0; virtual void saveState() = 0; virtual void restoreState() = 0; - virtual bool clipRegionIntersects (int x, int y, int w, int h) = 0; - virtual const Rectangle getClipBounds() const = 0; - virtual bool isClipEmpty() const = 0; - virtual void setColour (const Colour& colour) = 0; virtual void setGradient (const ColourGradient& gradient) = 0; + virtual void setTiledFill (const Image& image, int x, int y) = 0; + virtual void setOpacity (float opacity) = 0; virtual void setInterpolationQuality (Graphics::ResamplingQuality quality) = 0; - virtual void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) = 0; + virtual void fillRect (const Rectangle& r, const bool replaceExistingContents) = 0; virtual void fillPath (const Path& path, const AffineTransform& transform) = 0; - virtual void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY) = 0; - - virtual void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) = 0; - virtual void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY) = 0; - - virtual void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY) = 0; - - virtual void blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) = 0; + virtual void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) = 0; virtual void drawLine (double x1, double y1, double x2, double y2) = 0; virtual void drawVerticalLine (const int x, double top, double bottom) = 0; virtual void drawHorizontalLine (const int y, double left, double right) = 0; virtual void setFont (const Font& newFont) = 0; - virtual void drawGlyph (int glyphNumber, float x, float y) = 0; + virtual const Font getFont() = 0; virtual void drawGlyph (int glyphNumber, const AffineTransform& transform) = 0; }; @@ -40144,40 +39769,32 @@ public: void setOrigin (int x, int y); - bool reduceClipRegion (int x, int y, int w, int h); - bool reduceClipRegion (const RectangleList& clipRegion); - void excludeClipRegion (int x, int y, int w, int h); - + bool clipToRectangle (const Rectangle& r); + bool clipToRectangleList (const RectangleList& clipRegion); + void excludeClipRectangle (const Rectangle& r); void clipToPath (const Path& path, const AffineTransform& transform); - void clipToImage (Image& image, int imageX, int imageY); + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform); + + bool clipRegionIntersects (const Rectangle& r); + const Rectangle getClipBounds() const; + bool isClipEmpty() const; void saveState(); void restoreState(); - bool clipRegionIntersects (int x, int y, int w, int h); - const Rectangle getClipBounds() const; - bool isClipEmpty() const; - void setColour (const Colour& colour); void setGradient (const ColourGradient& gradient); + void setTiledFill (const Image& image, int x, int y); + void setOpacity (float opacity); void setInterpolationQuality (Graphics::ResamplingQuality quality); - void fillRect (int x, int y, int w, int h, const bool replaceExistingContents); + void fillAll (const bool replaceContents); + void fillRect (const Rectangle& r, const bool replaceExistingContents); void fillPath (const Path& path, const AffineTransform& transform); - void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY); - - void fillAlphaChannel (const Image& alphaImage, int imageX, int imageY); - void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY); - - void blendImage (const Image& sourceImage, int destX, int destY, int destW, int destH, - int sourceX, int sourceY); - - void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform); + void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles); void drawLine (double x1, double y1, double x2, double y2); @@ -40185,6 +39802,7 @@ public: void drawHorizontalLine (const int x, double top, double bottom); void setFont (const Font& newFont); + const Font getFont(); void drawGlyph (int glyphNumber, float x, float y); void drawGlyph (int glyphNumber, const AffineTransform& transform); @@ -40197,31 +39815,6 @@ protected: LLGCSavedState* currentState; OwnedArray stateStack; -/* void drawVertical (const int x, const double top, const double bottom); - void drawHorizontal (const int y, const double top, const double bottom); - - void clippedFillRectWithColour (const Rectangle& clipRect, int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); - - void clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform); - void clippedFillPathWithImage (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha); - - void clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY); - void clippedFillAlphaChannelWithImage (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, const float opacity); - - void clippedBlendImage (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY); - - void clippedBlendImageWarping (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform); - - void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2); - - void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); - void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom);*/ - LowLevelGraphicsSoftwareRenderer (const LowLevelGraphicsSoftwareRenderer& other); const LowLevelGraphicsSoftwareRenderer& operator= (const LowLevelGraphicsSoftwareRenderer&); }; @@ -40229,12 +39822,6 @@ protected: #endif // __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ /********* End of inlined file: juce_LowLevelGraphicsSoftwareRenderer.h *********/ -#endif -#ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ - -#endif -#ifndef __JUCE_GRAPHICS_JUCEHEADER__ - #endif #ifndef __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ @@ -40348,7 +39935,7 @@ protected: /********* End of inlined file: juce_LowLevelGraphicsPostScriptRenderer.h *********/ #endif -#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__ +#ifndef __JUCE_PATH_JUCEHEADER__ #endif #ifndef __JUCE_BORDERSIZE_JUCEHEADER__ @@ -40357,109 +39944,14 @@ protected: #ifndef __JUCE_LINE_JUCEHEADER__ #endif -#ifndef __JUCE_PATH_JUCEHEADER__ +#ifndef __JUCE_POINT_JUCEHEADER__ #endif -#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ - -/********* Start of inlined file: juce_PathIterator.h *********/ -#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ -#define __JUCE_PATHITERATOR_JUCEHEADER__ - -/** - Flattens a Path object into a series of straight-line sections. - - Use one of these to iterate through a Path object, and it will convert - all the curves into line sections so it's easy to render or perform - geometric operations on. - - @see Path -*/ -class JUCE_API PathFlatteningIterator -{ -public: - - /** Creates a PathFlatteningIterator. - - After creation, use the next() method to initialise the fields in the - object with the first line's position. - - @param path the path to iterate along - @param transform a transform to apply to each point in the path being iterated - @param tolerence the amount by which the curves are allowed to deviate from the - lines into which they are being broken down - a higher tolerence - is a bit faster, but less smooth. - */ - PathFlatteningIterator (const Path& path, - const AffineTransform& transform = AffineTransform::identity, - float tolerence = 6.0f) throw(); - - /** Destructor. */ - ~PathFlatteningIterator() throw(); - - /** Fetches the next line segment from the path. - - This will update the member variables x1, y1, x2, y2, subPathIndex and closesSubPath - so that they describe the new line segment. - - @returns false when there are no more lines to fetch. - */ - bool next() throw(); - - /** The x position of the start of the current line segment. */ - float x1; - /** The y position of the start of the current line segment. */ - float y1; - /** The x position of the end of the current line segment. */ - float x2; - /** The y position of the end of the current line segment. */ - float y2; - - /** Indicates whether the current line segment is closing a sub-path. - - If the current line is the one that connects the end of a sub-path - back to the start again, this will be true. - */ - bool closesSubPath; - - /** The index of the current line within the current sub-path. - - E.g. you can use this to see whether the line is the first one in the - subpath by seeing if it's 0. - */ - int subPathIndex; - - /** Returns true if the current segment is the last in the current sub-path. */ - bool isLastInSubpath() const throw() { return stackPos == stackBase - && (index >= path.numElements - || points [index] == Path::moveMarker); } - - juce_UseDebuggingNewOperator - -private: - const Path& path; - const AffineTransform transform; - float* points; - float tolerence, subPathCloseX, subPathCloseY; - bool isIdentityTransform; - - float* stackBase; - float* stackPos; - int index, stackSize; - - PathFlatteningIterator (const PathFlatteningIterator&); - const PathFlatteningIterator& operator= (const PathFlatteningIterator&); -}; - -#endif // __JUCE_PATHITERATOR_JUCEHEADER__ -/********* End of inlined file: juce_PathIterator.h *********/ +#ifndef __JUCE_RECTANGLE_JUCEHEADER__ #endif #ifndef __JUCE_PATHSTROKETYPE_JUCEHEADER__ -#endif -#ifndef __JUCE_POINT_JUCEHEADER__ - #endif #ifndef __JUCE_POSITIONEDRECTANGLE_JUCEHEADER__ @@ -40775,10 +40267,105 @@ private: /********* End of inlined file: juce_PositionedRectangle.h *********/ #endif -#ifndef __JUCE_RECTANGLE_JUCEHEADER__ +#ifndef __JUCE_RECTANGLELIST_JUCEHEADER__ #endif -#ifndef __JUCE_RECTANGLELIST_JUCEHEADER__ +#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ + +/********* Start of inlined file: juce_PathIterator.h *********/ +#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ +#define __JUCE_PATHITERATOR_JUCEHEADER__ + +/** + Flattens a Path object into a series of straight-line sections. + + Use one of these to iterate through a Path object, and it will convert + all the curves into line sections so it's easy to render or perform + geometric operations on. + + @see Path +*/ +class JUCE_API PathFlatteningIterator +{ +public: + + /** Creates a PathFlatteningIterator. + + After creation, use the next() method to initialise the fields in the + object with the first line's position. + + @param path the path to iterate along + @param transform a transform to apply to each point in the path being iterated + @param tolerence the amount by which the curves are allowed to deviate from the + lines into which they are being broken down - a higher tolerence + is a bit faster, but less smooth. + */ + PathFlatteningIterator (const Path& path, + const AffineTransform& transform = AffineTransform::identity, + float tolerence = 6.0f) throw(); + + /** Destructor. */ + ~PathFlatteningIterator() throw(); + + /** Fetches the next line segment from the path. + + This will update the member variables x1, y1, x2, y2, subPathIndex and closesSubPath + so that they describe the new line segment. + + @returns false when there are no more lines to fetch. + */ + bool next() throw(); + + /** The x position of the start of the current line segment. */ + float x1; + /** The y position of the start of the current line segment. */ + float y1; + /** The x position of the end of the current line segment. */ + float x2; + /** The y position of the end of the current line segment. */ + float y2; + + /** Indicates whether the current line segment is closing a sub-path. + + If the current line is the one that connects the end of a sub-path + back to the start again, this will be true. + */ + bool closesSubPath; + + /** The index of the current line within the current sub-path. + + E.g. you can use this to see whether the line is the first one in the + subpath by seeing if it's 0. + */ + int subPathIndex; + + /** Returns true if the current segment is the last in the current sub-path. */ + bool isLastInSubpath() const throw() { return stackPos == stackBase + && (index >= path.numElements + || points [index] == Path::moveMarker); } + + juce_UseDebuggingNewOperator + +private: + const Path& path; + const AffineTransform transform; + float* points; + float tolerence, subPathCloseX, subPathCloseY; + bool isIdentityTransform; + + float* stackBase; + float* stackPos; + int index, stackSize; + + PathFlatteningIterator (const PathFlatteningIterator&); + const PathFlatteningIterator& operator= (const PathFlatteningIterator&); +}; + +#endif // __JUCE_PATHITERATOR_JUCEHEADER__ +/********* End of inlined file: juce_PathIterator.h *********/ + +#endif +#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__ #endif #ifndef __JUCE_CAMERADEVICE_JUCEHEADER__ @@ -40909,9 +40496,6 @@ private: #endif // __JUCE_CAMERADEVICE_JUCEHEADER__ /********* End of inlined file: juce_CameraDevice.h *********/ -#endif -#ifndef __JUCE_IMAGE_JUCEHEADER__ - #endif #ifndef __JUCE_IMAGECACHE_JUCEHEADER__ @@ -41051,6 +40635,9 @@ private: #endif // __JUCE_IMAGECACHE_JUCEHEADER__ /********* End of inlined file: juce_ImageCache.h *********/ +#endif +#ifndef __JUCE_IMAGE_JUCEHEADER__ + #endif #ifndef __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ @@ -41743,6 +41330,78 @@ private: #endif // __JUCE_DRAWABLEIMAGE_JUCEHEADER__ /********* End of inlined file: juce_DrawableImage.h *********/ +#endif +#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ + +/********* Start of inlined file: juce_DrawableText.h *********/ +#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ +#define __JUCE_DRAWABLETEXT_JUCEHEADER__ + +/** + A drawable object which renders a line of text. + + @see Drawable +*/ +class JUCE_API DrawableText : public Drawable +{ +public: + + /** Creates a DrawableText object. */ + DrawableText(); + + /** Destructor. */ + virtual ~DrawableText(); + + /** Sets the block of text to render */ + void setText (const GlyphArrangement& newText); + + /** Sets a single line of text to render. + + This is a convenient method of adding a single line - for + more complex text, use the setText() that takes a + GlyphArrangement instead. + */ + void setText (const String& newText, const Font& fontToUse); + + /** Returns the text arrangement that was set with setText(). */ + const GlyphArrangement& getText() const throw() { return text; } + + /** Sets the colour of the text. */ + void setColour (const Colour& newColour); + + /** Returns the current text colour. */ + const Colour& getColour() const throw() { return colour; } + + /** @internal */ + void render (const Drawable::RenderingContext& context) const; + /** @internal */ + void getBounds (float& x, float& y, float& width, float& height) const; + /** @internal */ + bool hitTest (float x, float y) const; + /** @internal */ + Drawable* createCopy() const; + /** @internal */ + bool readBinary (InputStream& input); + /** @internal */ + bool writeBinary (OutputStream& output) const; + /** @internal */ + bool readXml (const XmlElement& xml); + /** @internal */ + void writeXml (XmlElement& xml) const; + + juce_UseDebuggingNewOperator + +private: + GlyphArrangement text; + Colour colour; + + DrawableText (const DrawableText&); + const DrawableText& operator= (const DrawableText&); +}; + +#endif // __JUCE_DRAWABLETEXT_JUCEHEADER__ +/********* End of inlined file: juce_DrawableText.h *********/ + #endif #ifndef __JUCE_DRAWABLEPATH_JUCEHEADER__ @@ -41849,78 +41508,6 @@ private: #endif // __JUCE_DRAWABLEPATH_JUCEHEADER__ /********* End of inlined file: juce_DrawablePath.h *********/ -#endif -#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ - -/********* Start of inlined file: juce_DrawableText.h *********/ -#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ -#define __JUCE_DRAWABLETEXT_JUCEHEADER__ - -/** - A drawable object which renders a line of text. - - @see Drawable -*/ -class JUCE_API DrawableText : public Drawable -{ -public: - - /** Creates a DrawableText object. */ - DrawableText(); - - /** Destructor. */ - virtual ~DrawableText(); - - /** Sets the block of text to render */ - void setText (const GlyphArrangement& newText); - - /** Sets a single line of text to render. - - This is a convenient method of adding a single line - for - more complex text, use the setText() that takes a - GlyphArrangement instead. - */ - void setText (const String& newText, const Font& fontToUse); - - /** Returns the text arrangement that was set with setText(). */ - const GlyphArrangement& getText() const throw() { return text; } - - /** Sets the colour of the text. */ - void setColour (const Colour& newColour); - - /** Returns the current text colour. */ - const Colour& getColour() const throw() { return colour; } - - /** @internal */ - void render (const Drawable::RenderingContext& context) const; - /** @internal */ - void getBounds (float& x, float& y, float& width, float& height) const; - /** @internal */ - bool hitTest (float x, float y) const; - /** @internal */ - Drawable* createCopy() const; - /** @internal */ - bool readBinary (InputStream& input); - /** @internal */ - bool writeBinary (OutputStream& output) const; - /** @internal */ - bool readXml (const XmlElement& xml); - /** @internal */ - void writeXml (XmlElement& xml) const; - - juce_UseDebuggingNewOperator - -private: - GlyphArrangement text; - Colour colour; - - DrawableText (const DrawableText&); - const DrawableText& operator= (const DrawableText&); -}; - -#endif // __JUCE_DRAWABLETEXT_JUCEHEADER__ -/********* End of inlined file: juce_DrawableText.h *********/ - #endif #ifndef __JUCE_COMPONENT_JUCEHEADER__ @@ -45212,9 +44799,6 @@ private: #endif // __JUCE_CODEEDITORCOMPONENT_JUCEHEADER__ /********* End of inlined file: juce_CodeEditorComponent.h *********/ -#endif -#ifndef __JUCE_CODEDOCUMENT_JUCEHEADER__ - #endif #ifndef __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__ @@ -45259,6 +44843,9 @@ public: #endif // __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__ /********* End of inlined file: juce_CPlusPlusCodeTokeniser.h *********/ +#endif +#ifndef __JUCE_CODEDOCUMENT_JUCEHEADER__ + #endif #ifndef __JUCE_CODETOKENISER_JUCEHEADER__ @@ -48150,10 +47737,10 @@ private: /********* End of inlined file: juce_ToolbarItemPalette.h *********/ #endif -#ifndef __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ +#ifndef __JUCE_TREEVIEW_JUCEHEADER__ #endif -#ifndef __JUCE_TREEVIEW_JUCEHEADER__ +#ifndef __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ #endif #ifndef __JUCE_BOOLEANPROPERTYCOMPONENT_JUCEHEADER__ @@ -48347,10 +47934,10 @@ private: /********* End of inlined file: juce_ChoicePropertyComponent.h *********/ #endif -#ifndef __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ +#ifndef __JUCE_PROPERTYPANEL_JUCEHEADER__ #endif -#ifndef __JUCE_PROPERTYPANEL_JUCEHEADER__ +#ifndef __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ #endif #ifndef __JUCE_SLIDERPROPERTYCOMPONENT_JUCEHEADER__ @@ -51450,6 +51037,9 @@ private: #endif // __JUCE_FILECHOOSER_JUCEHEADER__ /********* End of inlined file: juce_FileChooser.h *********/ +#endif +#ifndef __JUCE_FILEFILTER_JUCEHEADER__ + #endif #ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ @@ -51570,9 +51160,6 @@ private: #endif // __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ /********* End of inlined file: juce_FileChooserDialogBox.h *********/ -#endif -#ifndef __JUCE_FILEFILTER_JUCEHEADER__ - #endif #ifndef __JUCE_FILELISTCOMPONENT_JUCEHEADER__ @@ -51644,6 +51231,78 @@ private: #endif #ifndef __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ +#endif +#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_FileTreeComponent.h *********/ +#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ +#define __JUCE_FILETREECOMPONENT_JUCEHEADER__ + +/** + A component that displays the files in a directory as a treeview. + + This implements the DirectoryContentsDisplayComponent base class so that + it can be used in a FileBrowserComponent. + + To attach a listener to it, use its DirectoryContentsDisplayComponent base + class and the FileBrowserListener class. + + @see DirectoryContentsList, FileListComponent +*/ +class JUCE_API FileTreeComponent : public TreeView, + public DirectoryContentsDisplayComponent +{ +public: + + /** Creates a listbox to show the contents of a specified directory. + */ + FileTreeComponent (DirectoryContentsList& listToShow); + + /** Destructor. */ + ~FileTreeComponent(); + + /** Returns the number of selected files in the tree. + */ + int getNumSelectedFiles() const throw() { return TreeView::getNumSelectedItems(); } + + /** Returns one of the files that the user has currently selected. + + Returns File::nonexistent if none is selected. + */ + const File getSelectedFile (int index) const throw(); + + /** Returns the first of the files that the user has currently selected. + + Returns File::nonexistent if none is selected. + */ + const File getSelectedFile() const; + + /** Scrolls the list to the top. */ + void scrollToTop(); + + /** Setting a name for this allows tree items to be dragged. + + The string that you pass in here will be returned by the getDragSourceDescription() + of the items in the tree. For more info, see TreeViewItem::getDragSourceDescription(). + */ + void setDragAndDropDescription (const String& description) throw(); + + /** Returns the last value that was set by setDragAndDropDescription(). + */ + const String& getDragAndDropDescription() const throw() { return dragAndDropDescription; } + + juce_UseDebuggingNewOperator + +private: + String dragAndDropDescription; + + FileTreeComponent (const FileTreeComponent&); + const FileTreeComponent& operator= (const FileTreeComponent&); +}; + +#endif // __JUCE_FILETREECOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_FileTreeComponent.h *********/ + #endif #ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ @@ -51745,78 +51404,6 @@ private: #endif // __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ /********* End of inlined file: juce_FileSearchPathListComponent.h *********/ -#endif -#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ - -/********* Start of inlined file: juce_FileTreeComponent.h *********/ -#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ -#define __JUCE_FILETREECOMPONENT_JUCEHEADER__ - -/** - A component that displays the files in a directory as a treeview. - - This implements the DirectoryContentsDisplayComponent base class so that - it can be used in a FileBrowserComponent. - - To attach a listener to it, use its DirectoryContentsDisplayComponent base - class and the FileBrowserListener class. - - @see DirectoryContentsList, FileListComponent -*/ -class JUCE_API FileTreeComponent : public TreeView, - public DirectoryContentsDisplayComponent -{ -public: - - /** Creates a listbox to show the contents of a specified directory. - */ - FileTreeComponent (DirectoryContentsList& listToShow); - - /** Destructor. */ - ~FileTreeComponent(); - - /** Returns the number of selected files in the tree. - */ - int getNumSelectedFiles() const throw() { return TreeView::getNumSelectedItems(); } - - /** Returns one of the files that the user has currently selected. - - Returns File::nonexistent if none is selected. - */ - const File getSelectedFile (int index) const throw(); - - /** Returns the first of the files that the user has currently selected. - - Returns File::nonexistent if none is selected. - */ - const File getSelectedFile() const; - - /** Scrolls the list to the top. */ - void scrollToTop(); - - /** Setting a name for this allows tree items to be dragged. - - The string that you pass in here will be returned by the getDragSourceDescription() - of the items in the tree. For more info, see TreeViewItem::getDragSourceDescription(). - */ - void setDragAndDropDescription (const String& description) throw(); - - /** Returns the last value that was set by setDragAndDropDescription(). - */ - const String& getDragAndDropDescription() const throw() { return dragAndDropDescription; } - - juce_UseDebuggingNewOperator - -private: - String dragAndDropDescription; - - FileTreeComponent (const FileTreeComponent&); - const FileTreeComponent& operator= (const FileTreeComponent&); -}; - -#endif // __JUCE_FILETREECOMPONENT_JUCEHEADER__ -/********* End of inlined file: juce_FileTreeComponent.h *********/ - #endif #ifndef __JUCE_FILENAMECOMPONENT_JUCEHEADER__ @@ -52008,52 +51595,6 @@ private: #endif // __JUCE_FILENAMECOMPONENT_JUCEHEADER__ /********* End of inlined file: juce_FilenameComponent.h *********/ -#endif -#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ - -/********* Start of inlined file: juce_ImagePreviewComponent.h *********/ -#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ -#define __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ - -/** - A simple preview component that shows thumbnails of image files. - - @see FileChooserDialogBox, FilePreviewComponent -*/ -class JUCE_API ImagePreviewComponent : public FilePreviewComponent, - private Timer -{ -public: - - /** Creates an ImagePreviewComponent. */ - ImagePreviewComponent(); - - /** Destructor. */ - ~ImagePreviewComponent(); - - /** @internal */ - void selectedFileChanged (const File& newSelectedFile); - /** @internal */ - void paint (Graphics& g); - /** @internal */ - void timerCallback(); - - juce_UseDebuggingNewOperator - -private: - File fileToLoad; - Image* currentThumbnail; - String currentDetails; - - void getThumbSize (int& w, int& h) const; - - ImagePreviewComponent (const ImagePreviewComponent&); - const ImagePreviewComponent& operator= (const ImagePreviewComponent&); -}; - -#endif // __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ -/********* End of inlined file: juce_ImagePreviewComponent.h *********/ - #endif #ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ @@ -52104,6 +51645,52 @@ private: #endif // __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ /********* End of inlined file: juce_WildcardFileFilter.h *********/ +#endif +#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_ImagePreviewComponent.h *********/ +#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ +#define __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ + +/** + A simple preview component that shows thumbnails of image files. + + @see FileChooserDialogBox, FilePreviewComponent +*/ +class JUCE_API ImagePreviewComponent : public FilePreviewComponent, + private Timer +{ +public: + + /** Creates an ImagePreviewComponent. */ + ImagePreviewComponent(); + + /** Destructor. */ + ~ImagePreviewComponent(); + + /** @internal */ + void selectedFileChanged (const File& newSelectedFile); + /** @internal */ + void paint (Graphics& g); + /** @internal */ + void timerCallback(); + + juce_UseDebuggingNewOperator + +private: + File fileToLoad; + Image* currentThumbnail; + String currentDetails; + + void getThumbSize (int& w, int& h) const; + + ImagePreviewComponent (const ImagePreviewComponent&); + const ImagePreviewComponent& operator= (const ImagePreviewComponent&); +}; + +#endif // __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_ImagePreviewComponent.h *********/ + #endif #ifndef __JUCE_ALERTWINDOW_JUCEHEADER__ @@ -54382,64 +53969,6 @@ private: #endif // __JUCE_PREFERENCESPANEL_JUCEHEADER__ /********* End of inlined file: juce_PreferencesPanel.h *********/ -#endif -#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ - -/********* Start of inlined file: juce_SystemTrayIconComponent.h *********/ -#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ -#define __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ - -#if JUCE_WINDOWS || JUCE_LINUX || DOXYGEN - -/** - On Windows only, this component sits in the taskbar tray as a small icon. - - To use it, just create one of these components, but don't attempt to make it - visible, add it to a parent, or put it on the desktop. - - You can then call setIconImage() to create an icon for it in the taskbar. - - To change the icon's tooltip, you can use setIconTooltip(). - - To respond to mouse-events, you can override the normal mouseDown(), - mouseUp(), mouseDoubleClick() and mouseMove() methods, and although the x, y - position will not be valid, you can use this to respond to clicks. Traditionally - you'd use a left-click to show your application's window, and a right-click - to show a pop-up menu. -*/ -class JUCE_API SystemTrayIconComponent : public Component -{ -public: - - SystemTrayIconComponent(); - - /** Destructor. */ - ~SystemTrayIconComponent(); - - /** Changes the image shown in the taskbar. - */ - void setIconImage (const Image& newImage); - - /** Changes the tooltip that Windows shows above the icon. */ - void setIconTooltip (const String& tooltip); - -#if JUCE_LINUX - /** @internal */ - void paint (Graphics& g); -#endif - - juce_UseDebuggingNewOperator - -private: - - SystemTrayIconComponent (const SystemTrayIconComponent&); - const SystemTrayIconComponent& operator= (const SystemTrayIconComponent&); -}; - -#endif -#endif // __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ -/********* End of inlined file: juce_SystemTrayIconComponent.h *********/ - #endif #ifndef __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ @@ -54546,6 +54075,64 @@ private: #endif // __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ /********* End of inlined file: juce_WebBrowserComponent.h *********/ +#endif +#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ + +/********* Start of inlined file: juce_SystemTrayIconComponent.h *********/ +#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ +#define __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ + +#if JUCE_WINDOWS || JUCE_LINUX || DOXYGEN + +/** + On Windows only, this component sits in the taskbar tray as a small icon. + + To use it, just create one of these components, but don't attempt to make it + visible, add it to a parent, or put it on the desktop. + + You can then call setIconImage() to create an icon for it in the taskbar. + + To change the icon's tooltip, you can use setIconTooltip(). + + To respond to mouse-events, you can override the normal mouseDown(), + mouseUp(), mouseDoubleClick() and mouseMove() methods, and although the x, y + position will not be valid, you can use this to respond to clicks. Traditionally + you'd use a left-click to show your application's window, and a right-click + to show a pop-up menu. +*/ +class JUCE_API SystemTrayIconComponent : public Component +{ +public: + + SystemTrayIconComponent(); + + /** Destructor. */ + ~SystemTrayIconComponent(); + + /** Changes the image shown in the taskbar. + */ + void setIconImage (const Image& newImage); + + /** Changes the tooltip that Windows shows above the icon. */ + void setIconTooltip (const String& tooltip); + +#if JUCE_LINUX + /** @internal */ + void paint (Graphics& g); +#endif + + juce_UseDebuggingNewOperator + +private: + + SystemTrayIconComponent (const SystemTrayIconComponent&); + const SystemTrayIconComponent& operator= (const SystemTrayIconComponent&); +}; + +#endif +#endif // __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_SystemTrayIconComponent.h *********/ + #endif #ifndef __JUCE_QUICKTIMEMOVIECOMPONENT_JUCEHEADER__ diff --git a/src/gui/graphics/brushes/juce_Brush.cpp b/src/gui/graphics/brushes/juce_Brush.cpp deleted file mode 100644 index 41d8d2f4d6..0000000000 --- a/src/gui/graphics/brushes/juce_Brush.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#include "../../../core/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - - -#include "juce_Brush.h" -#include "../contexts/juce_Graphics.h" -#include "../contexts/juce_EdgeTable.h" - - -//============================================================================== -Brush::Brush() throw() -{ -} - -Brush::~Brush() throw() -{ -} - -void Brush::paintVerticalLine (LowLevelGraphicsContext& context, - int x, float y1, float y2) throw() -{ - Path p; - p.addRectangle ((float) x, y1, 1.0f, y2 - y1); - paintPath (context, p, AffineTransform::identity); -} - -void Brush::paintHorizontalLine (LowLevelGraphicsContext& context, - int y, float x1, float x2) throw() -{ - Path p; - p.addRectangle (x1, (float) y, x2 - x1, 1.0f); - paintPath (context, p, AffineTransform::identity); -} - -void Brush::paintLine (LowLevelGraphicsContext& context, - float x1, float y1, float x2, float y2) throw() -{ - Path p; - p.addLineSegment (x1, y1, x2, y2, 1.0f); - paintPath (context, p, AffineTransform::identity); -} - - -END_JUCE_NAMESPACE diff --git a/src/gui/graphics/brushes/juce_Brush.h b/src/gui/graphics/brushes/juce_Brush.h deleted file mode 100644 index 148be658d1..0000000000 --- a/src/gui/graphics/brushes/juce_Brush.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#ifndef __JUCE_BRUSH_JUCEHEADER__ -#define __JUCE_BRUSH_JUCEHEADER__ - -class Path; -class AffineTransform; -class LowLevelGraphicsContext; -class Image; -class Graphics; - - -//============================================================================== -/** - A brush is used to fill areas with colours, patterns, or images. - - The Graphics class has an idea of a current brush which it uses to render - shapes, rectangles, lines, text, etc. - - This is the base class - there are subclasses for useful types of fill pattern, - and applications can define their own brushes too. - - @see Graphics::setBrush, SolidColourBrush, GradientBrush, ImageBrush -*/ -class JUCE_API Brush -{ -protected: - //============================================================================== - /** Creates a Brush. - - (Nothing much happens in the base class). - */ - Brush() throw(); - -public: - /** Destructor. */ - virtual ~Brush() throw(); - - /** Creates a copy of whatever class of Brush this is. */ - virtual Brush* createCopy() const throw() = 0; - - /** Does whatever is relevent to transform the geometry of this brush. */ - virtual void applyTransform (const AffineTransform& transform) throw() = 0; - - /** Does whatever is relevent to change the opacity of this brush. */ - virtual void multiplyOpacity (const float multiple) throw() = 0; - - /** Must return true if this brush won't draw any pixels. */ - virtual bool isInvisible() const throw() = 0; - - //============================================================================== - virtual void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw() = 0; - - virtual void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw() = 0; - - virtual void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw() = 0; - - virtual void paintVerticalLine (LowLevelGraphicsContext& context, - int x, float y1, float y2) throw(); - - virtual void paintHorizontalLine (LowLevelGraphicsContext& context, - int y, float x1, float x2) throw(); - - virtual void paintLine (LowLevelGraphicsContext& context, - float x1, float y1, float x2, float y2) throw(); - -private: - //============================================================================== - Brush (const Brush&); - const Brush& operator= (const Brush&); -}; - -#endif // __JUCE_BRUSH_JUCEHEADER__ diff --git a/src/gui/graphics/brushes/juce_GradientBrush.cpp b/src/gui/graphics/brushes/juce_GradientBrush.cpp deleted file mode 100644 index 90645cf7ae..0000000000 --- a/src/gui/graphics/brushes/juce_GradientBrush.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#include "../../../core/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - - -#include "juce_GradientBrush.h" -#include "../contexts/juce_LowLevelGraphicsContext.h" - - -//============================================================================== -GradientBrush::GradientBrush (const Colour& colour1, - const float x1, - const float y1, - const Colour& colour2, - const float x2, - const float y2, - const bool isRadial) throw() - : gradient (colour1, x1, y1, - colour2, x2, y2, - isRadial) -{ -} - -GradientBrush::GradientBrush (const ColourGradient& gradient_) throw() - : gradient (gradient_) -{ -} - -GradientBrush::~GradientBrush() throw() -{ -} - -Brush* GradientBrush::createCopy() const throw() -{ - return new GradientBrush (gradient); -} - -void GradientBrush::applyTransform (const AffineTransform& transform) throw() -{ - gradient.transform = gradient.transform.followedBy (transform); -} - -void GradientBrush::multiplyOpacity (const float multiple) throw() -{ - gradient.multiplyOpacity (multiple); -} - -bool GradientBrush::isInvisible() const throw() -{ - return gradient.isInvisible(); -} - -//============================================================================== -void GradientBrush::paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw() -{ - context.setGradient (gradient); - context.fillPath (path, transform); -} - -void GradientBrush::paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw() -{ - context.setGradient (gradient); - context.fillRect (x, y, w, h, false); -} - -void GradientBrush::paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw() -{ - context.saveState(); - - if (context.reduceClipRegion (x, y, w, h)) - { - context.setGradient (gradient); - context.fillAlphaChannel (alphaChannelImage, imageX, imageY); - } - - context.restoreState(); -} - -END_JUCE_NAMESPACE diff --git a/src/gui/graphics/brushes/juce_GradientBrush.h b/src/gui/graphics/brushes/juce_GradientBrush.h deleted file mode 100644 index 89bf9883b9..0000000000 --- a/src/gui/graphics/brushes/juce_GradientBrush.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#ifndef __JUCE_GRADIENTBRUSH_JUCEHEADER__ -#define __JUCE_GRADIENTBRUSH_JUCEHEADER__ - -#include "juce_Brush.h" -#include "../colour/juce_ColourGradient.h" - - -//============================================================================== -/** - A Brush that fills areas with a colour gradient. - - The gradient can either be linear or circular. - - @see Brush, Graphics::setBrush, SolidColourBrush, ImageBrush -*/ -class JUCE_API GradientBrush : public Brush -{ -public: - //============================================================================== - /** Creates a gradient brush, ready for use in Graphics::setBrush(). - - (x1, y1) is the location relative to the origin of the Graphics context, - at which the colour should be colour1. Likewise for (x2, y2) and colour2. - - 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 the brush - need not be completely opaque. Note that this means 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 - grey colour, but Colour::transparentWhite to Colours::white will be - white all the way across. - - @see ColourGradient - */ - GradientBrush (const Colour& colour1, - const float x1, - const float y1, - const Colour& colour2, - const float x2, - const float y2, - const bool isRadial) throw(); - - /** Creates a gradient brush from a ColourGradient object. - */ - GradientBrush (const ColourGradient& gradient) throw(); - - /** Destructor. */ - ~GradientBrush() throw(); - - //============================================================================== - /** Returns the current gradient information */ - const ColourGradient& getGradient() const throw() { return gradient; } - - //============================================================================== - Brush* createCopy() const throw(); - - void applyTransform (const AffineTransform& transform) throw(); - - void multiplyOpacity (const float multiple) throw(); - - bool isInvisible() const throw(); - - void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw(); - - void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw(); - - void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw(); - - //============================================================================== - juce_UseDebuggingNewOperator - - -protected: - ColourGradient gradient; - -private: - GradientBrush (const GradientBrush&); - const GradientBrush& operator= (const GradientBrush&); -}; - -#endif // __JUCE_GRADIENTBRUSH_JUCEHEADER__ diff --git a/src/gui/graphics/brushes/juce_ImageBrush.cpp b/src/gui/graphics/brushes/juce_ImageBrush.cpp deleted file mode 100644 index 706a875e74..0000000000 --- a/src/gui/graphics/brushes/juce_ImageBrush.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#include "../../../core/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - - -#include "juce_ImageBrush.h" -#include "../contexts/juce_LowLevelGraphicsContext.h" - - -//============================================================================== -ImageBrush::ImageBrush (Image* const image_, - const int anchorX_, - const int anchorY_, - const float opacity_) throw() - : image (image_), - anchorX (anchorX_), - anchorY (anchorY_), - opacity (opacity_) -{ - jassert (image != 0); // not much point creating a brush without an image, is there? - - if (image != 0) - { - if (image->getWidth() == 0 || image->getHeight() == 0) - { - jassertfalse // you've passed in an empty image - not exactly brilliant for tiling. - image = 0; - } - } -} - -ImageBrush::~ImageBrush() throw() -{ -} - -Brush* ImageBrush::createCopy() const throw() -{ - return new ImageBrush (image, anchorX, anchorY, opacity); -} - -void ImageBrush::multiplyOpacity (const float multiple) throw() -{ - opacity *= multiple; -} - -bool ImageBrush::isInvisible() const throw() -{ - return opacity == 0.0f; -} - -void ImageBrush::applyTransform (const AffineTransform& /*transform*/) throw() -{ - //xxx should probably be smarter and warp the image -} - -void ImageBrush::getStartXY (int& x, int& y) const throw() -{ - x -= anchorX; - y -= anchorY; - - const int iw = image->getWidth(); - const int ih = image->getHeight(); - - if (x < 0) - x = ((x / iw) - 1) * iw; - else - x = (x / iw) * iw; - - if (y < 0) - y = ((y / ih) - 1) * ih; - else - y = (y / ih) * ih; - - - x += anchorX; - y += anchorY; -} - -//============================================================================== -void ImageBrush::paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw() -{ - context.saveState(); - - if (image != 0 && context.reduceClipRegion (x, y, w, h)) - { - const int right = x + w; - const int bottom = y + h; - - const int iw = image->getWidth(); - const int ih = image->getHeight(); - - int startX = x; - getStartXY (startX, y); - - while (y < bottom) - { - x = startX; - - while (x < right) - { - context.setOpacity (opacity); - context.blendImage (*image, x, y, iw, ih, 0, 0); - x += iw; - } - - y += ih; - } - } - - context.restoreState(); -} - -void ImageBrush::paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw() -{ - if (image != 0) - { - Rectangle clip (context.getClipBounds()); - context.setOpacity (opacity); - - { - float x, y, w, h; - path.getBoundsTransformed (transform, x, y, w, h); - - clip = clip.getIntersection (Rectangle ((int) floorf (x), - (int) floorf (y), - 2 + (int) floorf (w), - 2 + (int) floorf (h))); - } - - int x = clip.getX(); - int y = clip.getY(); - const int right = clip.getRight(); - const int bottom = clip.getBottom(); - - const int iw = image->getWidth(); - const int ih = image->getHeight(); - - int startX = x; - getStartXY (startX, y); - - while (y < bottom) - { - x = startX; - - while (x < right) - { - context.fillPathWithImage (path, transform, *image, x, y); - x += iw; - } - - y += ih; - } - } -} - -void ImageBrush::paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw() -{ - context.saveState(); - - if (image != 0 && context.reduceClipRegion (x, y, w, h)) - { - context.setOpacity (opacity); - const Rectangle clip (context.getClipBounds()); - x = clip.getX(); - y = clip.getY(); - const int right = clip.getRight(); - const int bottom = clip.getBottom(); - - const int iw = image->getWidth(); - const int ih = image->getHeight(); - - int startX = x; - getStartXY (startX, y); - - while (y < bottom) - { - x = startX; - - while (x < right) - { - context.fillAlphaChannelWithImage (alphaChannelImage, - imageX, imageY, *image, - x, y); - x += iw; - } - - y += ih; - } - } - - context.restoreState(); -} - - -END_JUCE_NAMESPACE diff --git a/src/gui/graphics/brushes/juce_ImageBrush.h b/src/gui/graphics/brushes/juce_ImageBrush.h deleted file mode 100644 index 9c136e4da9..0000000000 --- a/src/gui/graphics/brushes/juce_ImageBrush.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#ifndef __JUCE_IMAGEBRUSH_JUCEHEADER__ -#define __JUCE_IMAGEBRUSH_JUCEHEADER__ - -#include "juce_Brush.h" -#include "../imaging/juce_Image.h" - - -//============================================================================== -/** - A Brush that fills areas with tiled repetitions of an image. - - @see Brush, Graphics::setBrush, SolidColourBrush, GradientBrush -*/ -class JUCE_API ImageBrush : public Brush -{ -public: - //============================================================================== - /* Creates an image brush, ready for use in Graphics::setBrush(). - - (x, y) is an anchor point for the top-left of the image - A reference to the image passed in will be kept, so don't delete - it within the lifetime of this object - */ - ImageBrush (Image* const image, - const int anchorX, - const int anchorY, - const float opacity) throw(); - - /** Destructor. */ - ~ImageBrush() throw(); - - //============================================================================== - /** Returns the image currently being used. */ - Image* getImage() const throw() { return image; } - - /** Returns the current anchor X position. */ - int getAnchorX() const throw() { return anchorX; } - - /** Returns the current anchor Y position. */ - int getAnchorY() const throw() { return anchorY; } - - /** Returns the current opacity. */ - float getOpacity() const throw() { return opacity; } - - //============================================================================== - Brush* createCopy() const throw(); - - void applyTransform (const AffineTransform& transform) throw(); - - void multiplyOpacity (const float multiple) throw(); - - bool isInvisible() const throw(); - - void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw(); - - void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw(); - - void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw(); - - //============================================================================== - juce_UseDebuggingNewOperator - -protected: - Image* image; - int anchorX, anchorY; - float opacity; - -private: - ImageBrush (const ImageBrush&); - const ImageBrush& operator= (const ImageBrush&); - - void getStartXY (int& x, int& y) const throw(); -}; - -#endif // __JUCE_IMAGEBRUSH_JUCEHEADER__ diff --git a/src/gui/graphics/brushes/juce_SolidColourBrush.cpp b/src/gui/graphics/brushes/juce_SolidColourBrush.cpp deleted file mode 100644 index 41fb234aa7..0000000000 --- a/src/gui/graphics/brushes/juce_SolidColourBrush.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#include "../../../core/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - - -#include "juce_SolidColourBrush.h" -#include "../contexts/juce_LowLevelGraphicsContext.h" - - -//============================================================================== -SolidColourBrush::SolidColourBrush() throw() - : colour (0xff000000) -{ -} - -SolidColourBrush::SolidColourBrush (const Colour& colour_) throw() - : colour (colour_) -{ -} - -SolidColourBrush::~SolidColourBrush() throw() -{ -} - -Brush* SolidColourBrush::createCopy() const throw() -{ - return new SolidColourBrush (colour); -} - -void SolidColourBrush::applyTransform (const AffineTransform& /*transform*/) throw() -{ -} - -void SolidColourBrush::multiplyOpacity (const float multiple) throw() -{ - colour = colour.withMultipliedAlpha (multiple); -} - -bool SolidColourBrush::isInvisible() const throw() -{ - return colour.isTransparent(); -} - -void SolidColourBrush::paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw() -{ - context.setColour (colour); - context.fillPath (path, transform); -} - -void SolidColourBrush::paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw() -{ - context.setColour (colour); - context.fillRect (x, y, w, h, false); -} - -void SolidColourBrush::paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw() -{ - if (! colour.isTransparent()) - { - context.saveState(); - - if (context.reduceClipRegion (x, y, w, h)) - { - context.setColour (colour); - context.fillAlphaChannel (alphaChannelImage, imageX, imageY); - } - - context.restoreState(); - } -} - -void SolidColourBrush::paintVerticalLine (LowLevelGraphicsContext& context, - int x, float y1, float y2) throw() -{ - context.setColour (colour); - context.drawVerticalLine (x, y1, y2); -} - -void SolidColourBrush::paintHorizontalLine (LowLevelGraphicsContext& context, - int y, float x1, float x2) throw() -{ - context.setColour (colour); - context.drawHorizontalLine (y, x1, x2); -} - -void SolidColourBrush::paintLine (LowLevelGraphicsContext& context, - float x1, float y1, float x2, float y2) throw() -{ - context.setColour (colour); - context.drawLine (x1, y1, x2, y2); -} - - -END_JUCE_NAMESPACE diff --git a/src/gui/graphics/brushes/juce_SolidColourBrush.h b/src/gui/graphics/brushes/juce_SolidColourBrush.h deleted file mode 100644 index 8f0f850f1e..0000000000 --- a/src/gui/graphics/brushes/juce_SolidColourBrush.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-9 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#ifndef __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ -#define __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ - -#include "juce_Brush.h" -#include "../colour/juce_Colour.h" - - -//============================================================================== -/** - A Brush that fills its area with a solid (or semi-transparent) colour. - - An application won't normally need to use this class directly, as drawing - with solid colours is taken care of automatically by the Graphics class - (it actually uses one of these brushes internally when you set the colour - with the Graphics::setColour() method). - - @see Brush, Graphics::setBrush, GradientBrush, ImageBrush -*/ -class JUCE_API SolidColourBrush : public Brush -{ -public: - //============================================================================== - /** Creates a SolidColourBrush to draw with the given colour. - - The colour can be changed later with the setColour() method. - */ - SolidColourBrush (const Colour& colour) throw(); - - /** Creates a SolidColourBrush set to black. - - The colour can be changed later with the setColour() method. - */ - SolidColourBrush() throw(); - - /** Destructor. */ - ~SolidColourBrush() throw(); - - //============================================================================== - /** Returns the colour currently being used. */ - const Colour& getColour() const throw() { return colour; } - - /** Sets the colour to use for drawing. */ - void setColour (const Colour& newColour) throw() { colour = newColour; } - - //============================================================================== - Brush* createCopy() const throw(); - - void applyTransform (const AffineTransform& transform) throw(); - - bool isInvisible() const throw(); - - void multiplyOpacity (const float multiple) throw(); - - void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw(); - - void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw(); - - void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw(); - - void paintVerticalLine (LowLevelGraphicsContext& context, - int x, float y1, float y2) throw(); - - void paintHorizontalLine (LowLevelGraphicsContext& context, - int y, float x1, float x2) throw(); - - void paintLine (LowLevelGraphicsContext& context, - float x1, float y1, float x2, float y2) throw(); - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - Colour colour; - - SolidColourBrush (const SolidColourBrush&); - const SolidColourBrush& operator= (const SolidColourBrush&); -}; - -#endif // __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ diff --git a/src/gui/graphics/colour/juce_Colour.cpp b/src/gui/graphics/colour/juce_Colour.cpp index d96c9db824..95968bada6 100644 --- a/src/gui/graphics/colour/juce_Colour.cpp +++ b/src/gui/graphics/colour/juce_Colour.cpp @@ -301,12 +301,18 @@ const Colour Colour::overlaidWith (const Colour& src) const throw() const Colour Colour::interpolatedWith (const Colour& other, float proportionOfOther) const throw() { - const int amount = jlimit (0, 256, (int) (proportionOfOther * 256.0f)); + if (proportionOfOther <= 0) + return *this; - return Colour ((uint8) (getRed() + (((other.getRed() - getRed()) * amount) >> 8)), - (uint8) (getGreen() + (((other.getGreen() - getGreen()) * amount) >> 8)), - (uint8) (getBlue() + (((other.getBlue() - getBlue()) * amount) >> 8)), - (uint8) (getAlpha() + (((other.getAlpha() - getAlpha()) * amount) >> 8))); + if (proportionOfOther >= 1.0f) + return other; + + PixelARGB c1 (getPixelARGB()); + const PixelARGB c2 (other.getPixelARGB()); + c1.tween (c2, roundFloatToInt (proportionOfOther * 255.0f)); + c1.unpremultiply(); + + return Colour (c1.getARGB()); } //============================================================================== diff --git a/src/gui/graphics/contexts/juce_EdgeTable.cpp b/src/gui/graphics/contexts/juce_EdgeTable.cpp index baf851ff6e..d6f282225e 100644 --- a/src/gui/graphics/contexts/juce_EdgeTable.cpp +++ b/src/gui/graphics/contexts/juce_EdgeTable.cpp @@ -49,7 +49,8 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, const Path& path, const AffineTransform& transform) throw() : bounds (bounds_), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + needToCheckEmptinesss (true) { table = (int*) juce_malloc ((bounds.getHeight() + 1) * lineStrideElements * sizeof (int)); int* t = table; @@ -167,7 +168,8 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) throw() : bounds (rectangleToAdd), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + needToCheckEmptinesss (true) { table = (int*) juce_malloc (jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int)); *table = 0; @@ -190,7 +192,8 @@ EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) throw() EdgeTable::EdgeTable (const float x, const float y, const float w, const float h) throw() : bounds (Rectangle ((int) floorf (x), roundFloatToInt (y * 256.0f) >> 8, 2 + (int) w, 2 + (int) h)), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1) + lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), + needToCheckEmptinesss (true) { jassert (w > 0 && h > 0); table = (int*) juce_malloc (jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int)); @@ -274,6 +277,7 @@ const EdgeTable& EdgeTable::operator= (const EdgeTable& other) throw() bounds = other.bounds; maxEdgesPerLine = other.maxEdgesPerLine; lineStrideElements = other.lineStrideElements; + needToCheckEmptinesss = other.needToCheckEmptinesss; const int tableSize = jmax (1, bounds.getHeight()) * lineStrideElements * sizeof (int); table = (int*) juce_malloc (tableSize); @@ -358,6 +362,27 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) throw line[0]++; } +void EdgeTable::translate (float dx, int dy) throw() +{ + bounds.setPosition (bounds.getX() + (int) floorf (dx), bounds.getY() + dy); + + int* lineStart = table; + const int intDx = (int) (dx * 256.0f); + + for (int i = bounds.getHeight(); --i >= 0;) + { + int* line = lineStart; + lineStart += lineStrideElements; + int num = *line++; + + while (--num >= 0) + { + *line += intDx; + line += 2; + } + } +} + void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) throw() { jassert (y >= 0 && y < bounds.getHeight()); @@ -482,6 +507,7 @@ void EdgeTable::clipToRectangle (const Rectangle& r) throw() if (clipped.isEmpty()) { + needToCheckEmptinesss = false; bounds.setHeight (0); } else @@ -505,6 +531,8 @@ void EdgeTable::clipToRectangle (const Rectangle& r) throw() for (int i = top; i < bottom; ++i) intersectWithEdgeTableLine (i, rectLine); } + + needToCheckEmptinesss = true; } } @@ -523,6 +551,8 @@ void EdgeTable::excludeRectangle (const Rectangle& r) throw() for (int i = top; i < bottom; ++i) intersectWithEdgeTableLine (i, rectLine); + + needToCheckEmptinesss = true; } } @@ -532,6 +562,7 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) if (clipped.isEmpty()) { + needToCheckEmptinesss = false; bounds.setHeight (0); } else @@ -555,109 +586,74 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) intersectWithEdgeTableLine (i, otherLine); otherLine += other.lineStrideElements; } + + needToCheckEmptinesss = true; } } -void EdgeTable::clipToImageAlpha (const Image& image, int x, int y) throw() +void EdgeTable::clipLineToMask (int x, int y, uint8* mask, int maskStride, int numPixels) throw() { - const Rectangle clipped (bounds.getIntersection (Rectangle (x, y, image.getWidth(), image.getHeight()))); + y -= bounds.getY(); - if (clipped.isEmpty()) + if (y < 0 || y >= bounds.getHeight()) + return; + + needToCheckEmptinesss = true; + + if (numPixels <= 0) { + table [lineStrideElements * y] = 0; + return; + } + + int* tempLine = (int*) alloca ((numPixels * 2 + 4) * sizeof (int)); + int destIndex = 0, lastLevel = 0; + + while (--numPixels >= 0) + { + const int alpha = *mask; + mask += maskStride; + + if (alpha != lastLevel) + { + tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = alpha; + lastLevel = alpha; + } + + ++x; + } + + if (lastLevel > 0) + { + tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = 0; + } + + tempLine[0] = destIndex >> 1; + + intersectWithEdgeTableLine (y, tempLine); +} + +bool EdgeTable::isEmpty() throw() +{ + if (needToCheckEmptinesss) + { + needToCheckEmptinesss = false; + int* t = table; + + for (int i = bounds.getHeight(); --i >= 0;) + { + if (t[0] > 1) + return false; + + t += lineStrideElements; + } + bounds.setHeight (0); } - else - { - if (! image.hasAlphaChannel()) - { - clipToRectangle (clipped); - return; - } - const int top = clipped.getY() - bounds.getY(); - const int bottom = clipped.getBottom() - bounds.getY(); - - if (bottom < bounds.getHeight()) - bounds.setHeight (bottom); - - if (clipped.getRight() < bounds.getRight()) - bounds.setRight (clipped.getRight()); - - for (int i = top; --i >= 0;) - table [lineStrideElements * i] = 0; - - int imageX = clipped.getX() - x; - int imageY = clipped.getY() - y; - - int* temp = (int*) alloca ((clipped.getWidth() * 2 + 4) * sizeof (int)); - - Image::BitmapData srcData (image, imageX, imageY, clipped.getWidth(), clipped.getHeight()); - - for (int i = 0; i < clipped.getHeight(); ++i) - { - int destIndex = 0, lastLevel = 0; - const uint8* pixels = srcData.getLinePointer (i); - - if (image.getFormat() == Image::ARGB) - { - for (int px = 0; px < clipped.getWidth(); ++px) - { - const int alpha = ((const PixelARGB*) pixels)->getAlpha(); - pixels += srcData.pixelStride; - - if (alpha != lastLevel) - { - temp[++destIndex] = (clipped.getX() + px) << 8; - temp[++destIndex] = alpha; - lastLevel = alpha; - } - } - } - else - { - jassert (image.getFormat() == Image::SingleChannel); - - for (int px = 0; px < clipped.getWidth(); ++px) - { - const int alpha = *pixels; - pixels += srcData.pixelStride; - - if (alpha != lastLevel) - { - temp[++destIndex] = (clipped.getX() + px) << 8; - temp[++destIndex] = alpha; - lastLevel = alpha; - } - } - } - - if (lastLevel > 0) - { - temp[++destIndex] = clipped.getRight() << 8; - temp[++destIndex] = 0; - } - - temp[0] = destIndex >> 1; - - intersectWithEdgeTableLine (top + i, temp); - ++y; - } - } -} - -bool EdgeTable::isEmpty() const throw() -{ - int* t = table; - - for (int i = bounds.getHeight(); --i >= 0;) - { - if (t[0] > 1) - return false; - - t += lineStrideElements; - } - - return true; + return bounds.getHeight() == 0; } END_JUCE_NAMESPACE diff --git a/src/gui/graphics/contexts/juce_EdgeTable.h b/src/gui/graphics/contexts/juce_EdgeTable.h index f190297f8f..16f881d970 100644 --- a/src/gui/graphics/contexts/juce_EdgeTable.h +++ b/src/gui/graphics/contexts/juce_EdgeTable.h @@ -78,9 +78,10 @@ public: void clipToRectangle (const Rectangle& r) throw(); void excludeRectangle (const Rectangle& r) throw(); void clipToEdgeTable (const EdgeTable& other); - void clipToImageAlpha (const Image& image, int x, int y) throw(); - bool isEmpty() const throw(); + void clipLineToMask (int x, int y, uint8* mask, int maskStride, int numPixels) throw(); + bool isEmpty() throw(); const Rectangle& getMaximumBounds() const throw() { return bounds; } + void translate (float dx, int dy) throw(); /** Reduces the amount of space the table has allocated. @@ -192,6 +193,7 @@ private: int* table; Rectangle bounds; int maxEdgesPerLine, lineStrideElements; + bool needToCheckEmptinesss; void addEdgePoint (const int x, const int y, const int winding) throw(); void remapTableForNumEdges (const int newNumEdgesPerLine) throw(); diff --git a/src/gui/graphics/contexts/juce_Graphics.cpp b/src/gui/graphics/contexts/juce_Graphics.cpp index 251461de60..0b34985706 100644 --- a/src/gui/graphics/contexts/juce_Graphics.cpp +++ b/src/gui/graphics/contexts/juce_Graphics.cpp @@ -31,8 +31,6 @@ BEGIN_JUCE_NAMESPACE #include "../fonts/juce_GlyphArrangement.h" #include "../geometry/juce_PathStrokeType.h" #include "juce_LowLevelGraphicsContext.h" -#include "../brushes/juce_GradientBrush.h" -#include "../brushes/juce_ImageBrush.h" static const Graphics::ResamplingQuality defaultQuality = Graphics::mediumResamplingQuality; @@ -65,7 +63,6 @@ LowLevelGraphicsContext::~LowLevelGraphicsContext() Graphics::Graphics (Image& imageToDrawOnto) throw() : context (imageToDrawOnto.createLowLevelContext()), ownsContext (true), - state (new GraphicsState()), saveStatePending (false) { resetToDefaultState(); @@ -74,7 +71,6 @@ Graphics::Graphics (Image& imageToDrawOnto) throw() Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() : context (internalContext), ownsContext (false), - state (new GraphicsState()), saveStatePending (false) { resetToDefaultState(); @@ -82,8 +78,6 @@ Graphics::Graphics (LowLevelGraphicsContext* const internalContext) throw() Graphics::~Graphics() throw() { - delete state; - if (ownsContext) delete context; } @@ -106,20 +100,20 @@ bool Graphics::reduceClipRegion (const int x, const int y, const int w, const int h) throw() { saveStateIfPending(); - return context->reduceClipRegion (x, y, w, h); + return context->clipToRectangle (Rectangle (x, y, w, h)); } bool Graphics::reduceClipRegion (const RectangleList& clipRegion) throw() { saveStateIfPending(); - return context->reduceClipRegion (clipRegion); + return context->clipToRectangleList (clipRegion); } void Graphics::excludeClipRegion (const int x, const int y, const int w, const int h) throw() { saveStateIfPending(); - context->excludeClipRegion (x, y, w, h); + context->excludeClipRectangle (Rectangle (x, y, w, h)); } bool Graphics::isClipEmpty() const throw() @@ -141,29 +135,9 @@ void Graphics::saveState() throw() void Graphics::restoreState() throw() { if (saveStatePending) - { saveStatePending = false; - } else - { - const int stackSize = stateStack.size(); - - if (stackSize > 0) - { - context->restoreState(); - - delete state; - state = stateStack.getUnchecked (stackSize - 1); - - stateStack.removeLast (1, false); - } - else - { - // Trying to call restoreState() more times than you've called saveState() ! - // Be careful to correctly match each saveState() with exactly one call to restoreState(). - jassertfalse - } - } + context->restoreState(); } void Graphics::saveStateIfPending() throw() @@ -171,9 +145,7 @@ void Graphics::saveStateIfPending() throw() if (saveStatePending) { saveStatePending = false; - context->saveState(); - stateStack.add (new GraphicsState (*state)); } } @@ -187,15 +159,13 @@ void Graphics::setOrigin (const int newOriginX, bool Graphics::clipRegionIntersects (const int x, const int y, const int w, const int h) const throw() { - return context->clipRegionIntersects (x, y, w, h); + return context->clipRegionIntersects (Rectangle (x, y, w, h)); } //============================================================================== void Graphics::setColour (const Colour& newColour) throw() { saveStateIfPending(); - - deleteAndZero (state->brush); context->setColour (newColour); } @@ -205,75 +175,26 @@ void Graphics::setOpacity (const float newOpacity) throw() context->setOpacity (newOpacity); } -void Graphics::setBrush (const Brush* const newBrush) throw() -{ - saveStateIfPending(); - delete state->brush; - state->brush = 0; - - if (newBrush != 0) - { - const SolidColourBrush* cb = dynamic_cast (newBrush); - - if (cb != 0) - { - setColour (cb->getColour()); - } - else - { - const GradientBrush* gb = dynamic_cast (newBrush); - - if (gb != 0) - { - setGradientFill (gb->getGradient()); - } - else - { - state->brush = newBrush->createCopy(); - } - } - } -} - void Graphics::setGradientFill (const ColourGradient& gradient) throw() { saveStateIfPending(); - deleteAndZero (state->brush); context->setGradient (gradient); } -void Graphics::setTiledImageFill (Image& imageToUse, +void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity) throw() { saveStateIfPending(); - delete state->brush; - state->brush = new ImageBrush (&imageToUse, anchorX, anchorY, opacity); -} - -//============================================================================== -Graphics::GraphicsState::GraphicsState() throw() - : brush (0) -{ -} - -Graphics::GraphicsState::GraphicsState (const GraphicsState& other) throw() - : brush (other.brush != 0 ? other.brush->createCopy() : 0), - font (other.font) -{ -} - -Graphics::GraphicsState::~GraphicsState() throw() -{ - delete brush; + context->setTiledFill (imageToUse, anchorX, anchorY); + context->setOpacity (opacity); } //============================================================================== void Graphics::setFont (const Font& newFont) throw() { saveStateIfPending(); - state->font = newFont; context->setFont (newFont); } @@ -281,8 +202,9 @@ void Graphics::setFont (const float newFontHeight, const int newFontStyleFlags) throw() { saveStateIfPending(); - state->font.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0); - context->setFont (state->font); + Font f (context->getFont()); + f.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0); + context->setFont (f); } //============================================================================== @@ -294,7 +216,7 @@ void Graphics::drawSingleLineText (const String& text, && startX < context->getClipBounds().getRight()) { GlyphArrangement arr; - arr.addLineOfText (state->font, text, (float) startX, (float) baselineY); + arr.addLineOfText (context->getFont(), text, (float) startX, (float) baselineY); arr.draw (*this); } } @@ -305,7 +227,7 @@ void Graphics::drawTextAsPath (const String& text, if (text.isNotEmpty()) { GlyphArrangement arr; - arr.addLineOfText (state->font, text, 0.0f, 0.0f); + arr.addLineOfText (context->getFont(), text, 0.0f, 0.0f); arr.draw (*this, transform); } } @@ -319,7 +241,7 @@ void Graphics::drawMultiLineText (const String& text, && startX < context->getClipBounds().getRight()) { GlyphArrangement arr; - arr.addJustifiedText (state->font, text, + arr.addJustifiedText (context->getFont(), text, (float) startX, (float) baselineY, (float) maximumLineWidth, Justification::left); arr.draw (*this); @@ -334,11 +256,11 @@ void Graphics::drawText (const String& text, const Justification& justificationType, const bool useEllipsesIfTooBig) const throw() { - if (text.isNotEmpty() && context->clipRegionIntersects (x, y, width, height)) + if (text.isNotEmpty() && context->clipRegionIntersects (Rectangle (x, y, width, height))) { GlyphArrangement arr; - arr.addCurtailedLineOfText (state->font, text, + arr.addCurtailedLineOfText (context->getFont(), text, 0.0f, 0.0f, (float)width, useEllipsesIfTooBig); @@ -361,11 +283,11 @@ void Graphics::drawFittedText (const String& text, { if (text.isNotEmpty() && width > 0 && height > 0 - && context->clipRegionIntersects (x, y, width, height)) + && context->clipRegionIntersects (Rectangle (x, y, width, height))) { GlyphArrangement arr; - arr.addFittedText (state->font, text, + arr.addFittedText (context->getFont(), text, (float) x, (float) y, (float) width, (float) height, justification, @@ -385,18 +307,12 @@ void Graphics::fillRect (int x, // passing in a silly number can cause maths problems in rendering! ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); - if (state->brush == 0) - context->fillRect (x, y, width, height, false); - else - state->brush->paintRectangle (*context, x, y, width, height); + context->fillRect (Rectangle (x, y, width, height), false); } void Graphics::fillRect (const Rectangle& r) const throw() { - fillRect (r.getX(), - r.getY(), - r.getWidth(), - r.getHeight()); + context->fillRect (r, false); } void Graphics::fillRect (const float x, @@ -414,13 +330,7 @@ void Graphics::fillRect (const float x, void Graphics::setPixel (int x, int y) const throw() { - if (context->clipRegionIntersects (x, y, 1, 1)) - { - if (state->brush == 0) - context->fillRect (x, y, 1, 1, false); - else - state->brush->paintRectangle (*context, x, y, 1, 1); - } + context->fillRect (Rectangle (x, y, 1, 1), false); } void Graphics::fillAll() const throw() @@ -436,7 +346,7 @@ void Graphics::fillAll (const Colour& colourToUse) const throw() context->saveState(); context->setColour (colourToUse); - context->fillRect (clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight(), false); + context->fillRect (clip, false); context->restoreState(); } } @@ -447,24 +357,16 @@ void Graphics::fillPath (const Path& path, const AffineTransform& transform) const throw() { if ((! context->isClipEmpty()) && ! path.isEmpty()) - { - if (state->brush == 0) - context->fillPath (path, transform); - else - state->brush->paintPath (*context, path, transform); - } + context->fillPath (path, transform); } void Graphics::strokePath (const Path& path, const PathStrokeType& strokeType, const AffineTransform& transform) const throw() { -// if ((! state->colour.isTransparent()) || state->brush != 0) - { - Path stroke; - strokeType.createStrokedPath (stroke, path, transform); - fillPath (stroke); - } + Path stroke; + strokeType.createStrokedPath (stroke, path, transform); + fillPath (stroke); } //============================================================================== @@ -477,21 +379,10 @@ void Graphics::drawRect (const int x, // passing in a silly number can cause maths problems in rendering! ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (x, y, width, height); - if (state->brush == 0) - { - context->fillRect (x, y, width, lineThickness, false); - context->fillRect (x, y + lineThickness, lineThickness, height - lineThickness * 2, false); - context->fillRect (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2, false); - context->fillRect (x, y + height - lineThickness, width, lineThickness, false); - } - else - { - Brush& b = *(state->brush); - b.paintRectangle (*context, x, y, width, lineThickness); - b.paintRectangle (*context, x, y + lineThickness, lineThickness, height - lineThickness * 2); - b.paintRectangle (*context, x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2); - b.paintRectangle (*context, x, y + height - lineThickness, width, lineThickness); - } + context->fillRect (Rectangle (x, y, width, lineThickness), false); + context->fillRect (Rectangle (x, y + lineThickness, lineThickness, height - lineThickness * 2), false); + context->fillRect (Rectangle (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2), false); + context->fillRect (Rectangle (x, y + height - lineThickness, width, lineThickness), false); } void Graphics::drawRect (const float x, @@ -545,13 +436,13 @@ void Graphics::drawBevel (const int x, : oldOpacity; context->setColour (topLeftColour.withMultipliedAlpha (op)); - context->fillRect (x + i, y + i, width - i * 2, 1, false); + context->fillRect (Rectangle (x + i, y + i, width - i * 2, 1), false); context->setColour (topLeftColour.withMultipliedAlpha (op * 0.75f)); - context->fillRect (x + i, y + i + 1, 1, height - i * 2 - 2, false); + context->fillRect (Rectangle (x + i, y + i + 1, 1, height - i * 2 - 2), false); context->setColour (bottomRightColour.withMultipliedAlpha (op)); - context->fillRect (x + i, y + height - i - 1, width - i * 2, 1, false); + context->fillRect (Rectangle (x + i, y + height - i - 1, width - i * 2, 1), false); context->setColour (bottomRightColour.withMultipliedAlpha (op * 0.75f)); - context->fillRect (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2, false); + context->fillRect (Rectangle (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2), false); } context->restoreState(); @@ -667,7 +558,7 @@ void Graphics::fillCheckerBoard (int x, int y, if (colour1 == colour2) { context->setColour (colour1); - context->fillRect (x, y, width, height, false); + context->fillRect (Rectangle (x, y, width, height), false); } else { @@ -684,9 +575,7 @@ void Graphics::fillCheckerBoard (int x, int y, for (int xx = x; xx < right; xx += checkWidth) { context->setColour (((cx++ & 1) == 0) ? colour1 : colour2); - context->fillRect (xx, y, - jmin (checkWidth, right - xx), - jmin (checkHeight, bottom - y), + context->fillRect (Rectangle (xx, y, jmin (checkWidth, right - xx), jmin (checkHeight, bottom - y)), false); } @@ -702,30 +591,17 @@ void Graphics::fillCheckerBoard (int x, int y, //============================================================================== void Graphics::drawVerticalLine (const int x, float top, float bottom) const throw() { - if (state->brush == 0) - context->drawVerticalLine (x, top, bottom); - else - state->brush->paintVerticalLine (*context, x, top, bottom); + context->drawVerticalLine (x, top, bottom); } void Graphics::drawHorizontalLine (const int y, float left, float right) const throw() { - if (state->brush == 0) - context->drawHorizontalLine (y, left, right); - else - state->brush->paintHorizontalLine (*context, y, left, right); + context->drawHorizontalLine (y, left, right); } -void Graphics::drawLine (float x1, float y1, - float x2, float y2) const throw() +void Graphics::drawLine (float x1, float y1, float x2, float y2) const throw() { - if (! context->isClipEmpty()) - { - if (state->brush == 0) - context->drawLine (x1, y1, x2, y2); - else - state->brush->paintLine (*context, x1, y1, x2, y2); - } + context->drawLine (x1, y1, x2, y2); } void Graphics::drawLine (const float startX, @@ -864,126 +740,14 @@ void Graphics::drawImage (const Image* const imageToDraw, ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (dx, dy, dw, dh); ASSERT_COORDS_ARE_SENSIBLE_NUMBERS (sx, sy, sw, sh); - if (imageToDraw == 0 || ! context->clipRegionIntersects (dx, dy, dw, dh)) + if (! context->clipRegionIntersects (Rectangle (dx, dy, dw, dh))) return; - if (sw == dw && sh == dh) - { - if (sx < 0) - { - dx -= sx; - dw += sx; - sw += sx; - sx = 0; - } - - if (sx + sw > imageToDraw->getWidth()) - { - const int amount = sx + sw - imageToDraw->getWidth(); - dw -= amount; - sw -= amount; - } - - if (sy < 0) - { - dy -= sy; - dh += sy; - sh += sy; - sy = 0; - } - - if (sy + sh > imageToDraw->getHeight()) - { - const int amount = sy + sh - imageToDraw->getHeight(); - dh -= amount; - sh -= amount; - } - - if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) - return; - - if (fillAlphaChannelWithCurrentBrush) - { - if (state->brush == 0) - { - context->saveState(); - - if (context->reduceClipRegion (dx, dy, dw, dh)) - context->fillAlphaChannel (*imageToDraw, dx - sx, dy - sy); - - context->restoreState(); - } - else - { - state->brush->paintAlphaChannel (*context, *imageToDraw, - dx - sx, dy - sy, - dx, dy, dw, dh); - } - } - else - { - context->blendImage (*imageToDraw, - dx, dy, dw, dh, sx, sy); - } - } - else - { - if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) - return; - - if (fillAlphaChannelWithCurrentBrush) - { - if (imageToDraw->isRGB()) - { - fillRect (dx, dy, dw, dh); - } - else - { - int tx = dx; - int ty = dy; - int tw = dw; - int th = dh; - - if (context->getClipBounds().intersectRectangle (tx, ty, tw, th)) - { - Image temp (imageToDraw->getFormat(), tw, th, true); - Graphics g (temp); -//xxx g.setImageResamplingQuality (state->quality); - g.setOrigin (dx - tx, dy - ty); - - g.drawImage (imageToDraw, - 0, 0, dw, dh, - sx, sy, sw, sh, - false); - - if (state->brush == 0) - { - context->saveState(); - - if (context->reduceClipRegion (tx, ty, tw, th)) - context->fillAlphaChannel (temp, tx, ty); - - context->restoreState(); - } - else - { - state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); - } - } - } - } - else - { - context->blendImageWarping (*imageToDraw, - sx, sy, sw, sh, - AffineTransform::translation ((float) -sx, - (float) -sy) - .scaled (dw / (float) sw, - dh / (float) sh) - .translated ((float) dx, - (float) dy)); - } - } + drawImageTransformed (imageToDraw, sx, sy, sw, sh, + AffineTransform::translation ((float) -sx, (float) -sy) + .scaled (dw / (float) sw, dh / (float) sh) + .translated ((float) dx, (float) dy), + fillAlphaChannelWithCurrentBrush); } void Graphics::drawImageTransformed (const Image* const imageToDraw, @@ -994,72 +758,96 @@ void Graphics::drawImageTransformed (const Image* const imageToDraw, const AffineTransform& transform, const bool fillAlphaChannelWithCurrentBrush) const throw() { - if (imageToDraw != 0 - && (! context->isClipEmpty()) - && ! transform.isSingularity()) + if (imageToDraw != 0 && ! context->isClipEmpty()) { - if (transform.isIdentity()) + const Rectangle srcClip (Rectangle (sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight)); + + if (fillAlphaChannelWithCurrentBrush) { - drawImage (imageToDraw, - sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight, - sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight, - fillAlphaChannelWithCurrentBrush); - } - else if (fillAlphaChannelWithCurrentBrush) - { - Path p; - p.addRectangle ((float) sourceClipX, (float) sourceClipY, - (float) sourceClipWidth, (float) sourceClipHeight); - - p.applyTransform (transform); - - float dx, dy, dw, dh; - p.getBounds (dx, dy, dw, dh); - int tx = (int) dx; - int ty = (int) dy; - int tw = roundFloatToInt (dw) + 2; - int th = roundFloatToInt (dh) + 2; - - if (context->getClipBounds().intersectRectangle (tx, ty, tw, th)) - { - Image temp (imageToDraw->getFormat(), tw, th, true); - Graphics g (temp); -//xxx g.setImageResamplingQuality (state->quality); - - g.drawImageTransformed (imageToDraw, - sourceClipX, - sourceClipY, - sourceClipWidth, - sourceClipHeight, - transform.translated ((float) -tx, (float) -ty), - false); - - if (state->brush == 0) - { - context->saveState(); - - if (context->reduceClipRegion (tx, ty, tw, th)) - context->fillAlphaChannel (temp, tx, ty); - - context->restoreState(); - } - else - { - state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); - } - } + context->saveState(); + context->clipToImageAlpha (*imageToDraw, srcClip, transform); + fillAll(); + context->restoreState(); } else { - context->blendImageWarping (*imageToDraw, - sourceClipX, - sourceClipY, - sourceClipWidth, - sourceClipHeight, - transform); + context->drawImage (*imageToDraw, srcClip, transform, false); } } } +//============================================================================== +Graphics::FillType::FillType() throw() + : colour (0xff000000), gradient (0), + image (0), imageX (0), imageY (0) +{ +} + +Graphics::FillType::FillType (const Colour& colour_) throw() + : colour (colour_), gradient (0), + image (0), imageX (0), imageY (0) +{ +} + +Graphics::FillType::FillType (const ColourGradient& gradient) throw() + : colour (0xff000000), gradient (new ColourGradient (gradient)), + image (0), imageX (0), imageY (0) +{ +} + +Graphics::FillType::FillType (Image* const image_, const int x, const int y) throw() + : colour (0xff000000), gradient (0), + image (image_), imageX (x), imageY (y) +{ +} + +Graphics::FillType::FillType (const FillType& other) throw() + : colour (other.colour), + gradient (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0), + image (other.image), imageX (other.imageX), imageY (other.imageY) +{ +} + +const Graphics::FillType& Graphics::FillType::operator= (const FillType& other) throw() +{ + if (this != &other) + { + colour = other.colour; + delete gradient; + gradient = (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0); + image = other.image; + imageX = other.imageX; + imageY = other.imageY; + } + + return *this; +} + +Graphics::FillType::~FillType() throw() +{ + delete gradient; +} + +void Graphics::FillType::setColour (const Colour& newColour) throw() +{ + deleteAndZero (gradient); + colour = newColour; +} + +void Graphics::FillType::setGradient (const ColourGradient& newGradient) throw() +{ + if (gradient != 0) + *gradient = newGradient; + else + gradient = new ColourGradient (newGradient); +} + +void Graphics::FillType::setTiledImage (const Image& image_, const int imageX_, const int imageY_) throw() +{ + deleteAndZero (gradient); + image = &image_; + imageX = imageX_; + imageY = imageY_; +} END_JUCE_NAMESPACE diff --git a/src/gui/graphics/contexts/juce_Graphics.h b/src/gui/graphics/contexts/juce_Graphics.h index 5cd75ee459..e853d18055 100644 --- a/src/gui/graphics/contexts/juce_Graphics.h +++ b/src/gui/graphics/contexts/juce_Graphics.h @@ -32,7 +32,6 @@ #include "../geometry/juce_Line.h" #include "../colour/juce_Colours.h" #include "../colour/juce_ColourGradient.h" -#include "../brushes/juce_SolidColourBrush.h" #include "juce_RectanglePlacement.h" class LowLevelGraphicsContext; class Image; @@ -79,7 +78,7 @@ public: 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, setBrush + @see setOpacity */ void setColour (const Colour& newColour) throw(); @@ -94,18 +93,6 @@ public: */ void setOpacity (const float newOpacity) throw(); - /** Changes the current brush to use for drawing. - - If a null pointer is passed in, the context will revert to using a solid - colour for drawing (using the last colour set by setColour()). - - If a brush is passed in, a copy of it will be used for subsequent drawing - operations until setColour() or setBrush() is called. - - @see SolidColourBrush, GradientBrush, ImageBrush, Brush - */ - void setBrush (const Brush* const newBrush) throw(); - /** Sets the context to use a gradient for its fill pattern. */ void setGradientFill (const ColourGradient& gradient) throw(); @@ -114,7 +101,7 @@ public: Make sure that you don't delete this image while it's still being used by this context! */ - void setTiledImageFill (Image& imageToUse, + void setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity) throw(); @@ -698,12 +685,13 @@ public: LowLevelGraphicsContext* getInternalContext() const throw() { return context; } //============================================================================== - /*class FillType + class FillType { public: + FillType() throw(); FillType (const Colour& colour) throw(); FillType (const ColourGradient& gradient) throw(); - FillType (Image* image, int x, int y) throw(); + FillType (Image* const image, const int x, const int y) throw(); FillType (const FillType& other) throw(); const FillType& operator= (const FillType& other) throw(); ~FillType() throw(); @@ -714,35 +702,22 @@ public: void setColour (const Colour& newColour) throw(); void setGradient (const ColourGradient& newGradient) throw(); - void setTiledImage (Image* image, const int imageX, const int imageY) throw(); + void setTiledImage (const Image& image, const int imageX, const int imageY) throw(); Colour colour; ColourGradient* gradient; - Image* image; + const Image* image; int imageX, imageY; juce_UseDebuggingNewOperator - };*/ + }; private: //============================================================================== LowLevelGraphicsContext* const context; const bool ownsContext; - struct GraphicsState - { - GraphicsState() throw(); - GraphicsState (const GraphicsState&) throw(); - ~GraphicsState() throw(); - - Brush* brush; - Font font; - }; - - GraphicsState* state; - OwnedArray stateStack; bool saveStatePending; - void saveStateIfPending() throw(); const Graphics& operator= (const Graphics& other); diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h index a350b11649..456d52180d 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsContext.h @@ -66,57 +66,40 @@ public: */ virtual void setOrigin (int x, int y) = 0; - /** Cliping co-ords are relative to the origin. */ - virtual bool reduceClipRegion (int x, int y, int w, int h) = 0; + virtual bool clipToRectangle (const Rectangle& r) = 0; + virtual bool clipToRectangleList (const RectangleList& clipRegion) = 0; + virtual void excludeClipRectangle (const Rectangle& r) = 0; + virtual void clipToPath (const Path& path, const AffineTransform& transform) = 0; + virtual void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) = 0; - /** Cliping co-ords are relative to the origin. */ - virtual bool reduceClipRegion (const RectangleList& clipRegion) = 0; - - //virtual bool clipToPath (const Path& path) = 0; - //virtual bool clipToImageAlpha (Image& image, int imageX, int imageY) = 0; - - /** Cliping co-ords are relative to the origin. */ - virtual void excludeClipRegion (int x, int y, int w, int h) = 0; + virtual bool clipRegionIntersects (const Rectangle& r) = 0; + virtual const Rectangle getClipBounds() const = 0; + virtual bool isClipEmpty() const = 0; virtual void saveState() = 0; virtual void restoreState() = 0; - virtual bool clipRegionIntersects (int x, int y, int w, int h) = 0; - virtual const Rectangle getClipBounds() const = 0; - virtual bool isClipEmpty() const = 0; - //============================================================================== virtual void setColour (const Colour& colour) = 0; virtual void setGradient (const ColourGradient& gradient) = 0; + virtual void setTiledFill (const Image& image, int x, int y) = 0; + virtual void setOpacity (float opacity) = 0; virtual void setInterpolationQuality (Graphics::ResamplingQuality quality) = 0; //============================================================================== - virtual void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) = 0; + virtual void fillRect (const Rectangle& r, const bool replaceExistingContents) = 0; virtual void fillPath (const Path& path, const AffineTransform& transform) = 0; - virtual void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY) = 0; + virtual void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) = 0; - virtual void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) = 0; - virtual void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY) = 0; - - //============================================================================== - virtual void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY) = 0; - - virtual void blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) = 0; - - //============================================================================== virtual void drawLine (double x1, double y1, double x2, double y2) = 0; virtual void drawVerticalLine (const int x, double top, double bottom) = 0; virtual void drawHorizontalLine (const int y, double left, double right) = 0; virtual void setFont (const Font& newFont) = 0; - virtual void drawGlyph (int glyphNumber, float x, float y) = 0; + virtual const Font getFont() = 0; virtual void drawGlyph (int glyphNumber, const AffineTransform& transform) = 0; }; diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index 6f3478999d..49f96c55e7 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -613,7 +613,10 @@ void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, float x, fl void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) { - font.renderGlyphIndirectly (*this, glyphNumber, transform); + Path p; + font.getTypeface()->getOutlineForGlyph (glyphNumber, p); + + fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform)); } END_JUCE_NAMESPACE diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index a461476482..5aac7a5e47 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -34,6 +34,7 @@ BEGIN_JUCE_NAMESPACE #include "../geometry/juce_PathStrokeType.h" #include "../geometry/juce_Rectangle.h" #include "../../../core/juce_SystemStats.h" +#include "../../../core/juce_Singleton.h" #include "../../../utilities/juce_DeletedAtShutdown.h" #if (JUCE_WINDOWS || JUCE_LINUX) && ! JUCE_64BIT @@ -49,151 +50,6 @@ BEGIN_JUCE_NAMESPACE #pragma warning (disable: 4127) // "expression is constant" warning #endif -//============================================================================== -/*static void replaceRectARGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() -{ - const PixelARGB blendColour (colour.getPixelARGB()); - - while (--h >= 0) - { - PixelARGB* const dest = (PixelARGB*) pixels; - - for (int i = 0; i < w; ++i) - dest[i] = blendColour; - - pixels += stride; - } -} - -static void blendRectRGB (uint8* pixels, const int w, int h, const int stride, const Colour& colour) throw() -{ - if (colour.isOpaque()) - { - replaceRectRGB (pixels, w, h, stride, colour); - } - else - { - const PixelARGB blendColour (colour.getPixelARGB()); - const int alpha = blendColour.getAlpha(); - - if (alpha <= 0) - return; - -#if JUCE_USE_SSE_INSTRUCTIONS - if (SystemStats::hasSSE()) - { - int64 rgb0 = (((int64) blendColour.getRed()) << 32) - | (int64) ((blendColour.getGreen() << 16) - | blendColour.getBlue()); - - const int invAlpha = 0xff - alpha; - int64 aaaa = (invAlpha << 16) | invAlpha; - aaaa = (aaaa << 16) | aaaa; - -#ifndef JUCE_GCC - __asm - { - movq mm1, aaaa - movq mm2, rgb0 - pxor mm7, mm7 - } - - while (--h >= 0) - { - __asm - { - mov edx, pixels - mov ebx, w - - pixloop: - prefetchnta [edx] - mov ax, [edx + 1] - shl eax, 8 - mov al, [edx] - movd mm0, eax - - punpcklbw mm0, mm7 - pmullw mm0, mm1 - psrlw mm0, 8 - paddw mm0, mm2 - packuswb mm0, mm7 - - movd eax, mm0 - mov [edx], al - inc edx - shr eax, 8 - mov [edx], ax - add edx, 2 - - dec ebx - jg pixloop - } - - pixels += stride; - } - - __asm emms -#else - __asm__ __volatile__ ( - "\tpush %%ebx \n" - "\tmovq %[aaaa], %%mm1 \n" - "\tmovq %[rgb0], %%mm2 \n" - "\tpxor %%mm7, %%mm7 \n" - ".lineLoop2: \n" - "\tmovl %%esi,%%edx \n" - "\tmovl %[w], %%ebx \n" - ".pixLoop2: \n" - "\tprefetchnta (%%edx) \n" - "\tmov (%%edx), %%ax \n" - "\tshl $8, %%eax \n" - "\tmov 2(%%edx), %%al \n" - "\tmovd %%eax, %%mm0 \n" - "\tpunpcklbw %%mm7, %%mm0 \n" - "\tpmullw %%mm1, %%mm0 \n" - "\tpsrlw $8, %%mm0 \n" - "\tpaddw %%mm2, %%mm0 \n" - "\tpackuswb %%mm7, %%mm0 \n" - "\tmovd %%mm0, %%eax \n" - "\tmovb %%al, (%%edx) \n" - "\tinc %%edx \n" - "\tshr $8, %%eax \n" - "\tmovw %%ax, (%%edx) \n" - "\tadd $2, %%edx \n" - "\tdec %%ebx \n" - "\tjg .pixLoop2 \n" - "\tadd %%edi, %%esi \n" - "\tdec %%ecx \n" - "\tjg .lineLoop2 \n" - "\tpop %%ebx \n" - "\temms \n" - : // No output registers - : [aaaa] "m" (aaaa), // Input registers - [rgb0] "m" (rgb0), - [w] "m" (w), - "c" (h), - [stride] "D" (stride), - [pixels] "S" (pixels) - : "cc", "eax", "edx", "memory" - ); -#endif - } - else -#endif - { - while (--h >= 0) - { - PixelRGB* dest = (PixelRGB*) pixels; - - for (int i = w; --i >= 0;) - (dest++)->blend (blendColour); - - pixels += stride; - } - } - } -} -*/ - //============================================================================== template class SolidColourEdgeTableRenderer @@ -317,10 +173,8 @@ private: class LinearGradientPixelGenerator { public: - LinearGradientPixelGenerator (const ColourGradient& gradient, - const PixelARGB* const lookupTable_, const int numEntries_) - : lookupTable (lookupTable_), - numEntries (numEntries_) + LinearGradientPixelGenerator (const ColourGradient& gradient, const PixelARGB* const lookupTable_, const int numEntries_) + : lookupTable (lookupTable_), numEntries (numEntries_) { jassert (numEntries_ >= 0); float x1 = gradient.x1; @@ -330,7 +184,7 @@ public: if (! gradient.transform.isIdentity()) { - Line l (x2, y2, x1, y1); + const Line l (x2, y2, x1, y1); const Point p3 = l.getPointAlongLine (0.0, 100.0f); float x3 = p3.getX(); float y3 = p3.getY(); @@ -339,8 +193,8 @@ public: gradient.transform.transformPoint (x2, y2); gradient.transform.transformPoint (x3, y3); - Line l2 (x2, y2, x3, y3); - float prop = l2.findNearestPointTo (x1, y1); + const Line l2 (x2, y2, x3, y3); + const float prop = l2.findNearestPointTo (x1, y1); const Point newP2 (l2.getPointAlongLineProportionally (prop)); x2 = newP2.getX(); @@ -379,10 +233,8 @@ public: forcedinline const PixelARGB getPixel (const int x) const throw() { - if (vertical) - return linePix; - - return lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; + return vertical ? linePix + : lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; } private: @@ -413,7 +265,8 @@ public: const float dx = gradient.x1 - gradient.x2; const float dy = gradient.y1 - gradient.y2; maxDist = dx * dx + dy * dy; - invScale = (numEntries + 1) / sqrt (maxDist); + invScale = numEntries / sqrt (maxDist); + jassert (roundDoubleToInt (sqrt (maxDist) * invScale) <= numEntries); } forcedinline void setY (const int y) throw() @@ -428,10 +281,7 @@ public: x *= x; x += dy; - if (x >= maxDist) - return lookupTable [numEntries]; - else - return lookupTable [jmin (numEntries, roundDoubleToInt (sqrt (x) * invScale))]; + return lookupTable [x >= maxDist ? numEntries : roundDoubleToInt (sqrt (x) * invScale)]; } protected: @@ -539,55 +389,98 @@ private: }; //============================================================================== -template +static forcedinline int safeModulo (int n, const int divisor) throw() +{ + jassert (divisor > 0); + n %= divisor; + return (n < 0) ? (n + divisor) : n; +} + +//============================================================================== +template class ImageFillEdgeTableRenderer { public: ImageFillEdgeTableRenderer (const Image::BitmapData& destData_, const Image::BitmapData& srcData_, - const int extraAlpha_) throw() + const int extraAlpha_, + const int x, const int y) throw() : destData (destData_), srcData (srcData_), - extraAlpha (extraAlpha_ + 1) + extraAlpha (extraAlpha_ + 1), + xOffset (repeatPattern ? safeModulo (x, srcData_.width) - srcData_.width : x), + yOffset (repeatPattern ? safeModulo (y, srcData_.height) - srcData_.height : y) { } forcedinline void setEdgeTableYPos (int y) throw() { linePixels = (DestPixelType*) destData.getLinePointer (y); + + y -= yOffset; + if (repeatPattern) + { + jassert (y >= 0); + y %= srcData.height; + } + sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y); } - forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const throw() + forcedinline void handleEdgeTablePixel (int x, int alphaLevel) const throw() { alphaLevel = (alphaLevel * extraAlpha) >> 8; - linePixels[x].blend (sourceLineStart [x], alphaLevel); + linePixels[x].blend (sourceLineStart [repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)], alphaLevel); } forcedinline void handleEdgeTableLine (int x, int width, int alphaLevel) const throw() { DestPixelType* dest = linePixels + x; alphaLevel = (alphaLevel * extraAlpha) >> 8; + x -= xOffset; + + jassert (repeatPattern || (x >= 0 && x + width <= srcData.width)); if (alphaLevel < 0xfe) { do { - dest++ ->blend (sourceLineStart [x++], alphaLevel); - + dest++ ->blend (sourceLineStart [repeatPattern ? (x++ % srcData.width) : x++], alphaLevel); } while (--width > 0); } else { - copyRow (dest, sourceLineStart + x, width); + if (repeatPattern) + { + do + { + dest++ ->blend (sourceLineStart [x++ % srcData.width]); + } while (--width > 0); + } + else + { + copyRow (dest, sourceLineStart + x, width); + } } } + void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) throw() + { + jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width); + SrcPixelType* sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y - yOffset); + uint8* mask = (uint8*) (sourceLineStart + x - xOffset); + + if (sizeof (SrcPixelType) == sizeof (PixelARGB)) + mask += PixelARGB::indexA; + + et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width); + } + private: const Image::BitmapData& destData; const Image::BitmapData& srcData; - const int extraAlpha; + const int extraAlpha, xOffset, yOffset; DestPixelType* linePixels; SrcPixelType* sourceLineStart; @@ -611,7 +504,7 @@ private: }; //============================================================================== -template +template class TransformedImageFillEdgeTableRenderer { public: @@ -674,6 +567,18 @@ public: } } + void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) throw() + { + uint8* mask = (uint8*) alloca (sizeof (SrcPixelType) * width); + y = y_; + generate ((SrcPixelType*) mask, x, width); + + if (sizeof (SrcPixelType) == sizeof (PixelARGB)) + mask += PixelARGB::indexA; + + et.clipLineToMask (x, y_, mask, sizeof (SrcPixelType), width); + } + private: //============================================================================== void generate (PixelARGB* dest, const int x, int numPixels) throw() @@ -690,6 +595,12 @@ private: int loResX = hiResX >> 8; int loResY = hiResY >> 8; + if (repeatPattern) + { + loResX = safeModulo (loResX, srcData.width); + loResY = safeModulo (loResY, srcData.height); + } + if (betterQuality && ((unsigned int) loResX) < (unsigned int) maxX && ((unsigned int) loResY) < (unsigned int) maxY) @@ -733,11 +644,14 @@ private: } else { - // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable - if (loResX < 0) loResX = 0; - if (loResY < 0) loResY = 0; - if (loResX > maxX) loResX = maxX; - if (loResY > maxY) loResY = maxY; + if (! repeatPattern) + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + } dest->set (*(const PixelARGB*) this->srcData.getPixelPointer (loResX, loResY)); } @@ -760,6 +674,12 @@ private: int loResX = hiResX >> 8; int loResY = hiResY >> 8; + if (repeatPattern) + { + loResX = safeModulo (loResX, srcData.width); + loResY = safeModulo (loResY, srcData.height); + } + if (betterQuality && ((unsigned int) loResX) < (unsigned int) maxX && ((unsigned int) loResY) < (unsigned int) maxY) @@ -799,11 +719,14 @@ private: } else { - // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable - if (loResX < 0) loResX = 0; - if (loResY < 0) loResY = 0; - if (loResX > maxX) loResX = maxX; - if (loResY > maxY) loResY = maxY; + if (! repeatPattern) + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + } dest->set (*(const PixelRGB*) this->srcData.getPixelPointer (loResX, loResY)); } @@ -826,6 +749,12 @@ private: int loResX = hiResX >> 8; int loResY = hiResY >> 8; + if (repeatPattern) + { + loResX = safeModulo (loResX, srcData.width); + loResY = safeModulo (loResY, srcData.height); + } + if (betterQuality && ((unsigned int) loResX) < (unsigned int) maxX && ((unsigned int) loResY) < (unsigned int) maxY) @@ -833,8 +762,8 @@ private: hiResX &= 255; hiResY &= 255; - uint32 c = 256 * 128; const uint8* src = this->srcData.getPixelPointer (loResX, loResY); + uint32 c = 256 * 128; c += src[0] * ((256 - hiResX) * (256 - hiResY)); c += src[1] * (hiResX * (256 - hiResY)); src += this->srcData.lineStride; @@ -845,11 +774,14 @@ private: } else { - // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable - if (loResX < 0) loResX = 0; - if (loResY < 0) loResY = 0; - if (loResX > maxX) loResX = maxX; - if (loResY > maxY) loResY = maxY; + if (! repeatPattern) + { + // Beyond the edges, just repeat the edge pixels and leave the anti-aliasing to be handled by the edgetable + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + } *((uint8*) dest) = *(this->srcData.getPixelPointer (loResX, loResY)); } @@ -953,46 +885,35 @@ private: class LLGCSavedState { public: - LLGCSavedState (const Rectangle& clip_, - const int xOffset_, const int yOffset_, - const Font& font_, const Colour& colour_, ColourGradient* const gradient_, + LLGCSavedState (const Rectangle& clip_, const int xOffset_, const int yOffset_, + const Font& font_, const Graphics::FillType& fillType_, const Graphics::ResamplingQuality interpolationQuality_) throw() : edgeTable (new EdgeTableHolder (EdgeTable (clip_))), - xOffset (xOffset_), - yOffset (yOffset_), - font (font_), - colour (colour_), - gradient (gradient_), + xOffset (xOffset_), yOffset (yOffset_), + font (font_), fillType (fillType_), interpolationQuality (interpolationQuality_) { } LLGCSavedState (const LLGCSavedState& other) throw() - : edgeTable (other.edgeTable), - xOffset (other.xOffset), - yOffset (other.yOffset), - font (other.font), - colour (other.colour), - gradient (other.gradient), - interpolationQuality (other.interpolationQuality) + : edgeTable (other.edgeTable), xOffset (other.xOffset), + yOffset (other.yOffset), font (other.font), + fillType (other.fillType), interpolationQuality (other.interpolationQuality) { - if (gradient != 0) - gradient = new ColourGradient (*gradient); } ~LLGCSavedState() throw() { - delete gradient; } - bool reduce (int x, int y, int w, int h) throw() + bool clipToRectangle (const Rectangle& r) throw() { dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.clipToRectangle (Rectangle (x, y, w, h)); + edgeTable->edgeTable.clipToRectangle (r); return ! edgeTable->edgeTable.isEmpty(); } - bool reduce (const RectangleList& r) throw() + bool clipToRectangleList (const RectangleList& r) throw() { dupeEdgeTableIfMultiplyReferenced(); RectangleList totalArea (edgeTable->edgeTable.getMaximumBounds()); @@ -1004,47 +925,34 @@ public: return ! edgeTable->edgeTable.isEmpty(); } - bool exclude (int x, int y, int w, int h) throw() + bool excludeClipRectangle (const Rectangle& r) throw() { dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.excludeRectangle (Rectangle (x, y, w, h)); + edgeTable->edgeTable.excludeRectangle (r); return ! edgeTable->edgeTable.isEmpty(); } - bool reduce (const Path& p, const AffineTransform& transform) throw() + void clipToPath (const Path& p, const AffineTransform& transform) throw() { dupeEdgeTableIfMultiplyReferenced(); EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); edgeTable->edgeTable.clipToEdgeTable (et); - return ! edgeTable->edgeTable.isEmpty(); - } - - bool reduce (const Image& image, int x, int y) throw() - { - dupeEdgeTableIfMultiplyReferenced(); - edgeTable->edgeTable.clipToImageAlpha (image, x, y); - return ! edgeTable->edgeTable.isEmpty(); - } - - bool reduce (const Image& image, const AffineTransform& transform) throw() - { - jassertfalse - return true; } + //============================================================================== void fillEdgeTable (Image& image, EdgeTable& et, const bool replaceContents = false) throw() { et.clipToEdgeTable (edgeTable->edgeTable); Image::BitmapData destData (image, 0, 0, image.getWidth(), image.getHeight(), true); - if (gradient != 0) + if (fillType.isGradient()) { jassert (! replaceContents); // that option is just for solid colours - ColourGradient g2 (*gradient); - + ColourGradient g2 (*(fillType.gradient)); const bool isIdentity = g2.transform.isOnlyTranslation(); + if (isIdentity) { // If our translation doesn't involve any distortion, we can speed it up.. @@ -1058,306 +966,182 @@ public: } else { - g2.transform = g2.transform.translated ((float) xOffset, - (float) yOffset); + g2.transform = g2.transform.translated ((float) xOffset, (float) yOffset); } int numLookupEntries; PixelARGB* const lookupTable = g2.createLookupTable (numLookupEntries); jassert (numLookupEntries > 0); - if (image.getFormat() == Image::RGB) + switch (image.getFormat()) { - jassert (destData.pixelStride == 3); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else if (image.getFormat() == Image::ARGB) - { - jassert (destData.pixelStride == 4); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (destData.pixelStride == 4); - - if (g2.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } + case Image::ARGB: renderGradient (et, destData, g2, lookupTable, numLookupEntries, isIdentity); break; + case Image::RGB: renderGradient (et, destData, g2, lookupTable, numLookupEntries, isIdentity); break; + default: renderGradient (et, destData, g2, lookupTable, numLookupEntries, isIdentity); break; } juce_free (lookupTable); } + else if (fillType.isTiledImage()) + { + renderImage (image, *(fillType.image), Rectangle (0, 0, fillType.image->getWidth(), fillType.image->getHeight()), + AffineTransform::translation ((float) fillType.imageX, (float) fillType.imageY), &et); + } else { - const PixelARGB fillColour (colour.getPixelARGB()); + const PixelARGB fillColour (fillType.colour.getPixelARGB()); - if (replaceContents) + switch (image.getFormat()) { - if (image.getFormat() == Image::RGB) - { - jassert (destData.pixelStride == 3); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - else if (image.getFormat() == Image::ARGB) - { - jassert (destData.pixelStride == 4); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (destData.pixelStride == 1); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - } - else - { - if (image.getFormat() == Image::RGB) - { - jassert (destData.pixelStride == 3); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - else if (image.getFormat() == Image::ARGB) - { - jassert (destData.pixelStride == 4); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } - else if (image.getFormat() == Image::SingleChannel) - { - jassert (destData.pixelStride == 1); - SolidColourEdgeTableRenderer renderer (destData, fillColour); - et.iterate (renderer); - } + case Image::ARGB: renderSolidFill2 (et, destData, fillColour, replaceContents); break; + case Image::RGB: renderSolidFill2 (et, destData, fillColour, replaceContents); break; + default: renderSolidFill2 (et, destData, fillColour, replaceContents); break; } } } - void renderImage (Image& destImage, const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t) throw() + //============================================================================== + void renderImage (Image& destImage, const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& t, const EdgeTable* const tiledFillClipRegion) throw() { - if (t.isSingularity()) - return; - - const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + jassert (Rectangle (0, 0, sourceImage.getWidth(), sourceImage.getHeight()).contains (srcClip)); + + const Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); + const Image::BitmapData srcData (sourceImage, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); + const int alpha = fillType.colour.getAlpha(); + const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); + if (transform.isOnlyTranslation()) { // If our translation doesn't involve any distortion, just use a simple blit.. - const int tx = (int) (t.getTranslationX() * 256.0f); - const int ty = (int) (t.getTranslationY() * 256.0f); + const int tx = (int) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); if ((! betterQuality) || ((tx | ty) & 224) == 0) { - renderImage (destImage, sourceImage, (tx + 128) >> 8, (ty + 128) >> 8); + const Rectangle srcRect (srcClip.translated ((tx + 128) >> 8, (ty + 128) >> 8)); + + if (tiledFillClipRegion != 0) + { + blittedRenderImage3 (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, srcRect.getX(), srcRect.getY()); + } + else + { + EdgeTable et (srcRect.getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); + et.clipToEdgeTable (edgeTable->edgeTable); + + if (! et.isEmpty()) + blittedRenderImage3 (sourceImage, destImage, et, destData, srcData, alpha, srcRect.getX(), srcRect.getY()); + } + return; } } - Path p; - p.addRectangle ((float) srcClipX, (float) srcClipY, (float) srcClipW, (float) srcClipH); - - EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); - et.clipToEdgeTable (edgeTable->edgeTable); - - Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); - Image::BitmapData srcData (sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - - const int extraAlpha = colour.getAlpha(); - - switch (sourceImage.getFormat()) - { - case Image::ARGB: - if (destImage.getFormat() == Image::ARGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else if (destImage.getFormat() == Image::RGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else - { - jassert (destImage.getFormat() == Image::SingleChannel) - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - break; - - case Image::RGB: - if (destImage.getFormat() == Image::ARGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else if (destImage.getFormat() == Image::RGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else - { - jassert (destImage.getFormat() == Image::SingleChannel) - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - break; - - default: - jassert (sourceImage.getFormat() == Image::SingleChannel); - - if (destImage.getFormat() == Image::ARGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else if (destImage.getFormat() == Image::RGB) - { - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - else - { - jassert (destImage.getFormat() == Image::SingleChannel) - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - break; - } - } - - void renderImage (Image& destImage, const Image& sourceImage, int imageX, int imageY) throw() - { - EdgeTable et (Rectangle (imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight()) - .getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); - et.clipToEdgeTable (edgeTable->edgeTable); - - if (et.isEmpty()) + if (transform.isSingularity()) return; - Image::BitmapData destData (destImage, 0, 0, destImage.getWidth(), destImage.getHeight(), true); - Image::BitmapData srcData (sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - srcData.data = srcData.getPixelPointer (-imageX, -imageY); - - const int alpha = colour.getAlpha(); - - switch (destImage.getFormat()) + if (tiledFillClipRegion != 0) { - case Image::RGB: - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - break; + transformedRenderImage3 (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, transform, betterQuality); + } + else + { + Path p; + p.addRectangle (srcClip); - case Image::ARGB: - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - break; + EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); + et.clipToEdgeTable (edgeTable->edgeTable); - default: - jassert (destImage.getFormat() == Image::SingleChannel); - if (sourceImage.getFormat() == Image::RGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else if (sourceImage.getFormat() == Image::ARGB) - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - else - { - ImageFillEdgeTableRenderer renderer (destData, srcData, alpha); - et.iterate (renderer); - } - break; + if (! et.isEmpty()) + transformedRenderImage3 (sourceImage, destImage, et, destData, srcData, alpha, transform, betterQuality); } } + //============================================================================== + void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& transform) throw() + { + if (! image.hasAlphaChannel()) + { + Path p; + p.addRectangle (srcClip); + clipToPath (p, transform); + return; + } + + dupeEdgeTableIfMultiplyReferenced(); + + const Image::BitmapData srcData (image, srcClip.getX(), srcClip.getY(), srcClip.getWidth(), srcClip.getHeight()); + const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); + EdgeTable& et = edgeTable->edgeTable; + + if (transform.isOnlyTranslation()) + { + // If our translation doesn't involve any distortion, just use a simple blit.. + const int tx = (int) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); + + if ((! betterQuality) || ((tx | ty) & 224) == 0) + { + const int imageX = ((tx + 128) >> 8); + const int imageY = ((ty + 128) >> 8); + + if (image.getFormat() == Image::ARGB) + straightClipImage (et, srcData, imageX, imageY); + else + straightClipImage (et, srcData, imageX, imageY); + + return; + } + } + + if (transform.isSingularity()) + { + et.clipToRectangle (Rectangle()); + return; + } + + { + Path p; + p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); + EdgeTable et2 (et.getMaximumBounds(), p, transform); + et.clipToEdgeTable (et2); + } + + if (! et.isEmpty()) + { + if (image.getFormat() == Image::ARGB) + transformedClipImage (et, srcData, transform, betterQuality); + else + transformedClipImage (et, srcData, transform, betterQuality); + } + } + + template + void transformedClipImage (EdgeTable& et, const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality) throw() + { + TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); + + for (int y = 0; y < et.getMaximumBounds().getHeight(); ++y) + renderer.clipEdgeTableLine (et, et.getMaximumBounds().getX(), y + et.getMaximumBounds().getY(), + et.getMaximumBounds().getWidth()); + } + + template + void straightClipImage (EdgeTable& et, const Image::BitmapData& srcData, int imageX, int imageY) throw() + { + Rectangle r (imageX, imageY, srcData.width, srcData.height); + et.clipToRectangle (r); + + ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); + + for (int y = 0; y < r.getHeight(); ++y) + renderer.clipEdgeTableLine (et, r.getX(), y + r.getY(), r.getWidth()); + } + + //============================================================================== class EdgeTableHolder : public ReferenceCountedObject { public: @@ -1371,8 +1155,7 @@ public: ReferenceCountedObjectPtr edgeTable; int xOffset, yOffset; Font font; - Colour colour; - ColourGradient* gradient; + Graphics::FillType fillType; Graphics::ResamplingQuality interpolationQuality; private: @@ -1383,6 +1166,117 @@ private: if (edgeTable->getReferenceCount() > 1) edgeTable = new EdgeTableHolder (edgeTable->edgeTable); } + + //============================================================================== + template + void renderGradient (EdgeTable& et, const Image::BitmapData& destData, const ColourGradient& g, + const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity) throw() + { + jassert (destData.pixelStride == sizeof (DestPixelType)); + + if (g.isRadial) + { + if (isIdentity) + { + GradientEdgeTableRenderer renderer (destData, g, lookupTable, numLookupEntries); + et.iterate (renderer); + } + else + { + GradientEdgeTableRenderer renderer (destData, g, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + else + { + GradientEdgeTableRenderer renderer (destData, g, lookupTable, numLookupEntries); + et.iterate (renderer); + } + } + + //============================================================================== + template + void renderSolidFill1 (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour) throw() + { + jassert (destData.pixelStride == sizeof (DestPixelType)); + SolidColourEdgeTableRenderer renderer (destData, fillColour); + et.iterate (renderer); + } + + template + void renderSolidFill2 (EdgeTable& et, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents) throw() + { + if (replaceContents) + renderSolidFill1 (et, destData, fillColour); + else + renderSolidFill1 (et, destData, fillColour); + } + + //============================================================================== + template + void transformedRenderImage1 (const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, const AffineTransform& transform, const bool betterQuality) throw() + { + TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, alpha, betterQuality); + et.iterate (renderer); + } + + template + void transformedRenderImage2 (Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, const AffineTransform& transform, const bool betterQuality) throw() + { + switch (destImage.getFormat()) + { + case Image::ARGB: transformedRenderImage1 (et, destData, srcData, alpha, transform, betterQuality); break; + case Image::RGB: transformedRenderImage1 (et, destData, srcData, alpha, transform, betterQuality); break; + default: transformedRenderImage1 (et, destData, srcData, alpha, transform, betterQuality); break; + } + } + + template + void transformedRenderImage3 (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, const AffineTransform& transform, const bool betterQuality) throw() + { + switch (srcImage.getFormat()) + { + case Image::ARGB: transformedRenderImage2 (destImage, et, destData, srcData, alpha, transform, betterQuality); break; + case Image::RGB: transformedRenderImage2 (destImage, et, destData, srcData, alpha, transform, betterQuality); break; + default: transformedRenderImage2 (destImage, et, destData, srcData, alpha, transform, betterQuality); break; + } + } + + //============================================================================== + template + void blittedRenderImage1 (const EdgeTable& et, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, int x, int y) throw() + { + ImageFillEdgeTableRenderer renderer (destData, srcData, alpha, x, y); + et.iterate (renderer); + } + + template + void blittedRenderImage2 (Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, + const Image::BitmapData& srcData, const int alpha, int x, int y) throw() + { + switch (destImage.getFormat()) + { + case Image::ARGB: blittedRenderImage1 (et, destData, srcData, alpha, x, y); break; + case Image::RGB: blittedRenderImage1 (et, destData, srcData, alpha, x, y); break; + default: blittedRenderImage1 (et, destData, srcData, alpha, x, y); break; + } + } + + template + void blittedRenderImage3 (const Image& srcImage, Image& destImage, const EdgeTable& et, const Image::BitmapData& destData, + const Image::BitmapData& srcData, const int alpha, int x, int y) throw() + { + switch (srcImage.getFormat()) + { + case Image::ARGB: blittedRenderImage2 (destImage, et, destData, srcData, alpha, x, y); break; + case Image::RGB: blittedRenderImage2 (destImage, et, destData, srcData, alpha, x, y); break; + default: blittedRenderImage2 (destImage, et, destData, srcData, alpha, x, y); break; + } + } }; @@ -1392,7 +1286,7 @@ LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (Image& image stateStack (20) { currentState = new LLGCSavedState (Rectangle (0, 0, image_.getWidth(), image_.getHeight()), - 0, 0, Font(), Colours::black, 0, Graphics::mediumResamplingQuality); + 0, 0, Font(), Graphics::FillType(), Graphics::mediumResamplingQuality); } LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() @@ -1412,36 +1306,38 @@ void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y) currentState->yOffset += y; } -bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (int x, int y, int w, int h) +bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle& r) { - return currentState->reduce (x + currentState->xOffset, y + currentState->yOffset, w, h); + return currentState->clipToRectangle (r.translated (currentState->xOffset, currentState->yOffset)); } -bool LowLevelGraphicsSoftwareRenderer::reduceClipRegion (const RectangleList& clipRegion) +bool LowLevelGraphicsSoftwareRenderer::clipToRectangleList (const RectangleList& clipRegion) { RectangleList temp (clipRegion); temp.offsetAll (currentState->xOffset, currentState->yOffset); - return currentState->reduce (temp); + return currentState->clipToRectangleList (temp); } -void LowLevelGraphicsSoftwareRenderer::excludeClipRegion (int x, int y, int w, int h) +void LowLevelGraphicsSoftwareRenderer::excludeClipRectangle (const Rectangle& r) { - currentState->exclude (x + currentState->xOffset, y + currentState->yOffset, w, h); + currentState->excludeClipRectangle (r.translated (currentState->xOffset, currentState->yOffset)); } void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform) { + currentState->clipToPath (path, transform.translated ((float) currentState->xOffset, (float) currentState->yOffset)); } -void LowLevelGraphicsSoftwareRenderer::clipToImage (Image& image, int imageX, int imageY) +void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) { + currentState->clipToImageAlpha (sourceImage, srcClip, transform.translated ((float) currentState->xOffset, (float) currentState->yOffset)); } -bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (int x, int y, int w, int h) +bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle& r) { return currentState->edgeTable->edgeTable.getMaximumBounds() - .intersects (Rectangle (x + currentState->xOffset, y + currentState->yOffset, w, h)); + .intersects (r.translated (currentState->xOffset, currentState->yOffset)); } const Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const @@ -1477,21 +1373,24 @@ void LowLevelGraphicsSoftwareRenderer::restoreState() } //============================================================================== -void LowLevelGraphicsSoftwareRenderer::setColour (const Colour& colour_) +void LowLevelGraphicsSoftwareRenderer::setColour (const Colour& colour) { - deleteAndZero (currentState->gradient); - currentState->colour = colour_; + currentState->fillType.setColour (colour); } -void LowLevelGraphicsSoftwareRenderer::setGradient (const ColourGradient& gradient_) +void LowLevelGraphicsSoftwareRenderer::setGradient (const ColourGradient& gradient) { - delete currentState->gradient; - currentState->gradient = new ColourGradient (gradient_); + currentState->fillType.setGradient (gradient); +} + +void LowLevelGraphicsSoftwareRenderer::setTiledFill (const Image& image, int x, int y) +{ + currentState->fillType.setTiledImage (image, x, y); } void LowLevelGraphicsSoftwareRenderer::setOpacity (float opacity) { - currentState->colour = currentState->colour.withAlpha (opacity); + currentState->fillType.colour = currentState->fillType.colour.withAlpha (opacity); } void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) @@ -1500,14 +1399,14 @@ void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::Resamp } //============================================================================== -void LowLevelGraphicsSoftwareRenderer::fillRect (int x, int y, int w, int h, const bool replaceExistingContents) +void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle& r, const bool replaceExistingContents) { - x += currentState->xOffset; - y += currentState->yOffset; + const Rectangle& totalClip = currentState->edgeTable->edgeTable.getMaximumBounds(); + const Rectangle clipped (totalClip.getIntersection (r.translated (currentState->xOffset, currentState->yOffset))); - if (Rectangle::intersectRectangles (x, y, w, h, 0, 0, image.getWidth(), image.getHeight())) + if (! clipped.isEmpty()) { - EdgeTable et (Rectangle (x, y, w, h)); + EdgeTable et (clipped); currentState->fillEdgeTable (image, et, replaceExistingContents); } } @@ -1521,62 +1420,11 @@ void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineT currentState->fillEdgeTable (image, et); } -void LowLevelGraphicsSoftwareRenderer::fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& sourceImage, int imageX, int imageY) +void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { - imageX += currentState->xOffset; - imageY += currentState->yOffset; - - saveState(); - currentState->reduce (path, transform.translated ((float) currentState->xOffset, (float) currentState->yOffset)); - currentState->renderImage (image, sourceImage, imageX, imageY); - restoreState(); -} - -void LowLevelGraphicsSoftwareRenderer::fillAlphaChannel (const Image& clipImage, int x, int y) -{ - x += currentState->xOffset; - y += currentState->yOffset; - - Rectangle maxBounds (currentState->edgeTable->edgeTable.getMaximumBounds()); - EdgeTable et (maxBounds.getIntersection (Rectangle (x, y, clipImage.getWidth(), clipImage.getHeight()))); - et.clipToImageAlpha (clipImage, x, y); - - currentState->fillEdgeTable (image, et); -} - -void LowLevelGraphicsSoftwareRenderer::fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY) -{ - alphaImageX += currentState->xOffset; - alphaImageY += currentState->yOffset; - fillerImageX += currentState->xOffset; - fillerImageY += currentState->yOffset; - - saveState(); - currentState->reduce (alphaImage, alphaImageX, alphaImageY); - currentState->renderImage (image, fillerImage, fillerImageX, fillerImageY); - restoreState(); -} - -//============================================================================== -void LowLevelGraphicsSoftwareRenderer::blendImage (const Image& sourceImage, int dx, int dy, int dw, int dh, int sx, int sy) -{ - dx += currentState->xOffset; - dy += currentState->yOffset; - - saveState(); - currentState->reduce (dx, dy, dw, dh); - currentState->renderImage (image, sourceImage, dx - sx, dy - sy); - restoreState(); -} - -//============================================================================== -void LowLevelGraphicsSoftwareRenderer::blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t) -{ - currentState->renderImage (image, sourceImage, srcClipX, srcClipY, srcClipW, srcClipH, t); + currentState->renderImage (image, sourceImage, srcClip, transform, + fillEntireClipAsTiles ? &(currentState->edgeTable->edgeTable) : 0); } //============================================================================== @@ -1601,19 +1449,175 @@ void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, double l } //============================================================================== +class GlyphCache : private DeletedAtShutdown +{ +public: + GlyphCache() throw() + : accessCounter (0), hits (0), misses (0) + { + enlargeCache (120); + } + + ~GlyphCache() throw() + { + clearSingletonInstance(); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); + + //============================================================================== + void drawGlyph (LLGCSavedState& state, Image& image, const Font& font, const int glyphNumber, float x, float y) throw() + { + ++accessCounter; + int oldestCounter = INT_MAX; + CachedGlyph* oldest = 0; + + for (int i = glyphs.size(); --i >= 0;) + { + CachedGlyph* const glyph = glyphs.getUnchecked (i); + + if (glyph->glyph == glyphNumber && glyph->font == font) + { + ++hits; + glyph->lastAccessCount = accessCounter; + glyph->draw (state, image, x, y); + return; + } + + if (glyph->lastAccessCount <= oldestCounter) + { + oldestCounter = glyph->lastAccessCount; + oldest = glyph; + } + } + + ++misses; + + if (hits + misses > (glyphs.size() << 4)) + { + if (misses * 2 > hits) + enlargeCache (glyphs.size() + 32); + + hits = 0; + misses = 0; + oldest = glyphs.getLast(); + } + + jassert (oldest != 0); + oldest->lastAccessCount = accessCounter; + oldest->generate (font, glyphNumber); + oldest->draw (state, image, x, y); + } + + //============================================================================== + class CachedGlyph + { + public: + CachedGlyph() throw() + : glyph (0), lastAccessCount (0) + { + edgeTable = 0; + } + + ~CachedGlyph() throw() + { + delete edgeTable; + } + + void draw (LLGCSavedState& state, Image& image, const float x, const float y) const throw() + { + if (edgeTable != 0) + { + EdgeTable et (*edgeTable); + et.translate (x, roundFloatToInt (y)); + state.fillEdgeTable (image, et, false); + } + } + + void generate (const Font& newFont, const int glyphNumber) throw() + { + font = newFont; + glyph = glyphNumber; + deleteAndZero (edgeTable); + + Path glyphPath; + font.getTypeface()->getOutlineForGlyph (glyphNumber, glyphPath); + + if (! glyphPath.isEmpty()) + { + const float fontHeight = font.getHeight(); + const AffineTransform transform (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)); + + float px, py, pw, ph; + glyphPath.getBoundsTransformed (transform, px, py, pw, ph); + + Rectangle clip ((int) floorf (px), (int) floorf (py), + roundFloatToInt (pw) + 2, roundFloatToInt (ph) + 2); + + edgeTable = new EdgeTable (clip, glyphPath, transform); + } + } + + int glyph, lastAccessCount; + Font font; + + //============================================================================== + juce_UseDebuggingNewOperator + + private: + EdgeTable* edgeTable; + + CachedGlyph (const CachedGlyph&); + const CachedGlyph& operator= (const CachedGlyph&); + }; + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + void enlargeCache (const int num) throw() + { + while (glyphs.size() < num) + glyphs.add (new CachedGlyph()); + } + + OwnedArray glyphs; + int accessCounter, hits, misses; + + GlyphCache (const GlyphCache&); + const GlyphCache& operator= (const GlyphCache&); +}; + +juce_ImplementSingleton_SingleThreaded (GlyphCache); + + void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) { currentState->font = newFont; } -void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, float x, float y) +const Font LowLevelGraphicsSoftwareRenderer::getFont() { - currentState->font.renderGlyphIndirectly (*this, glyphNumber, x, y); + return currentState->font; } void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) { - currentState->font.renderGlyphIndirectly (*this, glyphNumber, transform); + Font& f = currentState->font; + + if (transform.isOnlyTranslation()) + { + GlyphCache::getInstance()->drawGlyph (*currentState, image, f, glyphNumber, + transform.getTranslationX() + (float) currentState->xOffset, + roundFloatToInt (transform.getTranslationY() + (float) currentState->yOffset)); + } + else + { + Path p; + f.getTypeface()->getOutlineForGlyph (glyphNumber, p); + + fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()).followedBy (transform)); + } } #if JUCE_MSVC diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h index 7aedd21424..a88a2d4a70 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h @@ -49,45 +49,35 @@ public: //============================================================================== void setOrigin (int x, int y); - bool reduceClipRegion (int x, int y, int w, int h); - bool reduceClipRegion (const RectangleList& clipRegion); - void excludeClipRegion (int x, int y, int w, int h); - + bool clipToRectangle (const Rectangle& r); + bool clipToRectangleList (const RectangleList& clipRegion); + void excludeClipRectangle (const Rectangle& r); void clipToPath (const Path& path, const AffineTransform& transform); - void clipToImage (Image& image, int imageX, int imageY); + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform); + + bool clipRegionIntersects (const Rectangle& r); + const Rectangle getClipBounds() const; + bool isClipEmpty() const; void saveState(); void restoreState(); - bool clipRegionIntersects (int x, int y, int w, int h); - const Rectangle getClipBounds() const; - bool isClipEmpty() const; - //============================================================================== void setColour (const Colour& colour); void setGradient (const ColourGradient& gradient); + void setTiledFill (const Image& image, int x, int y); + void setOpacity (float opacity); void setInterpolationQuality (Graphics::ResamplingQuality quality); //============================================================================== - void fillRect (int x, int y, int w, int h, const bool replaceExistingContents); + void fillAll (const bool replaceContents); + void fillRect (const Rectangle& r, const bool replaceExistingContents); void fillPath (const Path& path, const AffineTransform& transform); - void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY); + void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles); - void fillAlphaChannel (const Image& alphaImage, int imageX, int imageY); - void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY); - - //============================================================================== - void blendImage (const Image& sourceImage, int destX, int destY, int destW, int destH, - int sourceX, int sourceY); - - void blendImageWarping (const Image& sourceImage, int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform); - - //============================================================================== void drawLine (double x1, double y1, double x2, double y2); void drawVerticalLine (const int x, double top, double bottom); @@ -95,6 +85,7 @@ public: //============================================================================== void setFont (const Font& newFont); + const Font getFont(); void drawGlyph (int glyphNumber, float x, float y); void drawGlyph (int glyphNumber, const AffineTransform& transform); @@ -108,33 +99,6 @@ protected: LLGCSavedState* currentState; OwnedArray stateStack; -/* void drawVertical (const int x, const double top, const double bottom); - void drawHorizontal (const int y, const double top, const double bottom); - - void clippedFillRectWithColour (const Rectangle& clipRect, int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents); - - void clippedFillPath (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform); - void clippedFillPathWithImage (int clipX, int clipY, int clipW, int clipH, const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY, float alpha); - - void clippedFillAlphaChannel (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY); - void clippedFillAlphaChannelWithImage (int clipX, int clipY, int clipW, int clipH, const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY, const float opacity); - - //============================================================================== - void clippedBlendImage (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY); - - void clippedBlendImageWarping (int clipX, int clipY, int clipW, int clipH, const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform); - - //============================================================================== - void clippedDrawLine (int clipX, int clipY, int clipW, int clipH, double x1, double y1, double x2, double y2); - - void clippedDrawVerticalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom); - void clippedDrawHorizontalLine (int clipX, int clipY, int clipW, int clipH, const int x, double top, double bottom);*/ - LowLevelGraphicsSoftwareRenderer (const LowLevelGraphicsSoftwareRenderer& other); const LowLevelGraphicsSoftwareRenderer& operator= (const LowLevelGraphicsSoftwareRenderer&); }; diff --git a/src/gui/graphics/fonts/juce_Font.cpp b/src/gui/graphics/fonts/juce_Font.cpp index 069582c929..08cbaba1f3 100644 --- a/src/gui/graphics/fonts/juce_Font.cpp +++ b/src/gui/graphics/fonts/juce_Font.cpp @@ -433,190 +433,4 @@ Typeface* Font::getTypeface() const throw() } -//============================================================================== -class FontGlyphAlphaMap -{ -public: - //============================================================================== - FontGlyphAlphaMap() throw() - : glyph (0), lastAccessCount (0) - { - bitmap[0] = bitmap[1] = 0; - } - - ~FontGlyphAlphaMap() throw() - { - delete bitmap[0]; - delete bitmap[1]; - } - - void draw (LowLevelGraphicsContext& g, float x, const float y) const throw() - { - if (bitmap[0] != 0) - { - const float xFloor = floorf (x); - const int bitmapToUse = ((x - xFloor) >= 0.5f && bitmap[1] != 0) ? 1 : 0; - - g.fillAlphaChannel (*bitmap [bitmapToUse], - xOrigin [bitmapToUse] + (int) xFloor, - yOrigin [bitmapToUse] + roundFloatToInt(y)); - } - } - - void generate (const Font& font_, const int glyph_) throw() - { - font = font_; - glyph = glyph_; - - deleteAndZero (bitmap[0]); - deleteAndZero (bitmap[1]); - - Path glyphPath; - font.getTypeface()->getOutlineForGlyph (glyph_, glyphPath); - - if (! glyphPath.isEmpty()) - { - const float fontHeight = font.getHeight(); - const float fontHScale = fontHeight * font.getHorizontalScale(); - AffineTransform transform (AffineTransform::scale (fontHScale, fontHeight)); - Rectangle clip (-2048, -2048, 4096, 4096), pos; - - bitmap[0] = glyphPath.createMaskBitmap (transform, clip, pos); - xOrigin[0] = pos.getX(); - yOrigin[0] = pos.getY(); - - if (fontHScale < 30.0f) - { - bitmap[1] = glyphPath.createMaskBitmap (transform.translated (0.5f, 0.0f), clip, pos); - xOrigin[1] = pos.getX(); - yOrigin[1] = pos.getY(); - } - } - } - - int glyph, lastAccessCount; - Font font; - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - Image* bitmap[2]; - int xOrigin[2], yOrigin[2]; - - FontGlyphAlphaMap (const FontGlyphAlphaMap&); - const FontGlyphAlphaMap& operator= (const FontGlyphAlphaMap&); -}; - - -//============================================================================== -class GlyphCache : private DeletedAtShutdown -{ -public: - GlyphCache() throw() - : accessCounter (0) - { - setCacheSize (120); - } - - ~GlyphCache() throw() - { - clearSingletonInstance(); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (GlyphCache); - - //============================================================================== - void drawGlyph (LowLevelGraphicsContext& g, const Font& font, int glyphNumber, float x, float y) throw() - { - ++accessCounter; - - int oldestCounter = INT_MAX; - FontGlyphAlphaMap* oldest = 0; - - for (int i = glyphs.size(); --i >= 0;) - { - FontGlyphAlphaMap* const glyph = glyphs.getUnchecked (i); - - if (glyph->glyph == glyphNumber - && glyph->font == font) - { - ++hits; - glyph->lastAccessCount = accessCounter; - glyph->draw (g, x, y); - return; - } - - if (glyph->lastAccessCount <= oldestCounter) - { - oldestCounter = glyph->lastAccessCount; - oldest = glyph; - } - } - - ++misses; - - if (hits + misses > (glyphs.size() << 4)) - { - if (misses * 2 > hits) - setCacheSize (glyphs.size() + 32); - - hits = 0; - misses = 0; - oldest = glyphs.getUnchecked (0); - } - - jassert (oldest != 0); - oldest->lastAccessCount = accessCounter; - oldest->generate (font, glyphNumber); - oldest->draw (g, x, y); - } - - void setCacheSize (int num) throw() - { - if (glyphs.size() != num) - { - glyphs.clear(); - - while (--num >= 0) - glyphs.add (new FontGlyphAlphaMap()); - - hits = 0; - misses = 0; - } - } - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - OwnedArray glyphs; - int accessCounter, hits, misses; - - GlyphCache (const GlyphCache&); - const GlyphCache& operator= (const GlyphCache&); -}; - -juce_ImplementSingleton_SingleThreaded (GlyphCache); - - -//============================================================================== -void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, float x, float y) -{ - if (font->height < 80.0f) - GlyphCache::getInstance()->drawGlyph (g, *this, glyphNumber, x, y); - else - renderGlyphIndirectly (g, glyphNumber, AffineTransform::translation (x, y)); -} - -void Font::renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, const AffineTransform& transform) -{ - Path p; - getTypeface()->getOutlineForGlyph (glyphNumber, p); - - g.fillPath (p, AffineTransform::scale (font->height * font->horizontalScale, font->height) - .followedBy (transform)); -} - - END_JUCE_NAMESPACE diff --git a/src/gui/graphics/fonts/juce_Font.h b/src/gui/graphics/fonts/juce_Font.h index bc3619feb8..81c6585497 100644 --- a/src/gui/graphics/fonts/juce_Font.h +++ b/src/gui/graphics/fonts/juce_Font.h @@ -307,24 +307,6 @@ public: */ void getGlyphPositions (const String& text, Array & glyphs, Array & xOffsets) const throw(); - //============================================================================== - /** Renders a glyph in a context without using methods other than the context's glyph-rendering - methods. - - For smaller fonts, this uses an internal cache of glyph images to speed things up, and renders - them using the context's image blending methods. For larger fonts, it gets the glyph's path - from the typeface and renders it as a shape. - - This method is primarily called by graphics contexts as a way of drawing a glyph if they can't do - it by native means. - */ - void renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, float x, float y); - - /** Renders a transformed glyph using path-filling techniques rather than calling a context's - actual glyph-rendering methods. - */ - void renderGlyphIndirectly (LowLevelGraphicsContext& g, int glyphNumber, const AffineTransform& transform); - //============================================================================== /** Returns the typeface used by this font. diff --git a/src/gui/graphics/fonts/juce_GlyphArrangement.cpp b/src/gui/graphics/fonts/juce_GlyphArrangement.cpp index 59d18d03e3..c26df75011 100644 --- a/src/gui/graphics/fonts/juce_GlyphArrangement.cpp +++ b/src/gui/graphics/fonts/juce_GlyphArrangement.cpp @@ -45,7 +45,7 @@ void PositionedGlyph::draw (const Graphics& g) const throw() if (! isWhitespace()) { g.getInternalContext()->setFont (font); - g.getInternalContext()->drawGlyph (glyph, x, y); + g.getInternalContext()->drawGlyph (glyph, AffineTransform::translation (x, y)); } } diff --git a/src/gui/graphics/geometry/juce_Path.cpp b/src/gui/graphics/geometry/juce_Path.cpp index cbb74264f6..1db31080db 100644 --- a/src/gui/graphics/geometry/juce_Path.cpp +++ b/src/gui/graphics/geometry/juce_Path.cpp @@ -323,11 +323,40 @@ const Point Path::getCurrentPosition() const void Path::addRectangle (const float x, const float y, const float w, const float h) throw() { - startNewSubPath (x, y + h); - lineTo (x, y); - lineTo (x + w, y); - lineTo (x + w, y + h); - closeSubPath(); + float x1 = x, y1 = y, x2 = x + w, y2 = y + h; + + if (w < 0) + swapVariables (x1, x2); + + if (h < 0) + swapVariables (y1, y2); + + ensureAllocatedSize (numElements + 13); + + elements [numElements++] = moveMarker; + elements [numElements++] = x1; + elements [numElements++] = y2; + elements [numElements++] = lineMarker; + elements [numElements++] = x1; + elements [numElements++] = y1; + elements [numElements++] = lineMarker; + elements [numElements++] = x2; + elements [numElements++] = y1; + elements [numElements++] = lineMarker; + elements [numElements++] = x2; + elements [numElements++] = y2; + elements [numElements++] = closeSubPathMarker; + + pathXMin = jmin (pathXMin, x1); + pathXMax = jmax (pathXMax, x2); + pathYMin = jmin (pathYMin, y1); + pathYMax = jmax (pathYMax, y2); +} + +void Path::addRectangle (const Rectangle& rectangle) throw() +{ + addRectangle ((float) rectangle.getX(), (float) rectangle.getY(), + (float) rectangle.getWidth(), (float) rectangle.getHeight()); } void Path::addRoundedRectangle (const float x, const float y, @@ -1549,70 +1578,5 @@ bool Path::Iterator::next() return false; } -//============================================================================== -class MaskBitmapRenderer -{ -public: - MaskBitmapRenderer (const Image::BitmapData& destData_) throw() - : destData (destData_) - { - } - - forcedinline void setEdgeTableYPos (const int y) throw() - { - lineStart = destData.getLinePointer (y); - } - - forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const throw() - { - lineStart [x] = (uint8) alphaLevel; - } - - forcedinline void handleEdgeTableLine (const int x, int width, const int alphaLevel) const throw() - { - uint8* d = lineStart + x; - - while (--width >= 0) - *d++ = (uint8) alphaLevel; - } - -private: - const Image::BitmapData& destData; - uint8* lineStart; - - MaskBitmapRenderer (const MaskBitmapRenderer&); - const MaskBitmapRenderer& operator= (const MaskBitmapRenderer&); -}; - -Image* Path::createMaskBitmap (const AffineTransform& transform, - const Rectangle& clipRegion, - Rectangle& imagePosition) const throw() -{ - if (isEmpty()) - return 0; - - float px, py, pw, ph; - getBoundsTransformed (transform, px, py, pw, ph); - - imagePosition = clipRegion.getIntersection (Rectangle ((int) floorf (px), (int) floorf (py), - roundFloatToInt (pw) + 2, roundFloatToInt (ph) + 2)); - - if (imagePosition.isEmpty()) - return 0; - - Image* im = Image::createNativeImage (Image::SingleChannel, imagePosition.getWidth(), imagePosition.getHeight(), true); - - EdgeTable edgeTable (Rectangle (0, 0, imagePosition.getWidth(), imagePosition.getHeight()), - *this, transform.translated ((float) -imagePosition.getX(), (float) -imagePosition.getY())); - - const Image::BitmapData destData (*im, 0, 0, imagePosition.getWidth(), imagePosition.getHeight(), true); - - jassert (destData.pixelStride == 1); - MaskBitmapRenderer renderer (destData); - edgeTable.iterate (renderer); - - return im; -} - END_JUCE_NAMESPACE diff --git a/src/gui/graphics/geometry/juce_Path.h b/src/gui/graphics/geometry/juce_Path.h index fe4d7d9a27..4f12671bee 100644 --- a/src/gui/graphics/geometry/juce_Path.h +++ b/src/gui/graphics/geometry/juce_Path.h @@ -221,6 +221,15 @@ public: void addRectangle (const float x, const float y, const float w, const float h) throw(); + /** Adds a rectangle to the path. + + The rectangle is added as a new sub-path. (Any currently open paths will be + left open). + + @see addRoundedRectangle, addTriangle + */ + void addRectangle (const Rectangle& rectangle) throw(); + /** Adds a rectangle with rounded corners to the path. The rectangle is added as a new sub-path. (Any currently open paths will be @@ -616,21 +625,6 @@ public: */ void restoreFromString (const String& stringVersion); - //============================================================================== - /** Creates a single-channel bitmap containing a mask of this path. - - The smallest bitmap that contains the path will be created, and on return, the - imagePosition rectangle indicates the position of the newly created image, relative - to the path's origin. - - Only the intersection of the path's bounds with the specified clipRegion rectangle - will be rendered. - - If the path is empty or doesn't intersect the clip region, this may return 0. - */ - Image* createMaskBitmap (const AffineTransform& transform, - const Rectangle& clipRegion, - Rectangle& imagePosition) const throw(); //============================================================================== juce_UseDebuggingNewOperator diff --git a/src/juce_amalgamated_template.cpp b/src/juce_amalgamated_template.cpp index 48baf39eaf..997e4eb8a0 100644 --- a/src/juce_amalgamated_template.cpp +++ b/src/juce_amalgamated_template.cpp @@ -284,10 +284,6 @@ #include "gui/components/windows/juce_ThreadWithProgressWindow.cpp" #include "gui/components/windows/juce_TooltipWindow.cpp" #include "gui/components/windows/juce_TopLevelWindow.cpp" -#include "gui/graphics/brushes/juce_Brush.cpp" -#include "gui/graphics/brushes/juce_GradientBrush.cpp" -#include "gui/graphics/brushes/juce_ImageBrush.cpp" -#include "gui/graphics/brushes/juce_SolidColourBrush.cpp" #include "gui/graphics/colour/juce_Colour.cpp" #include "gui/graphics/colour/juce_ColourGradient.cpp" #include "gui/graphics/colour/juce_Colours.cpp" diff --git a/src/juce_app_includes.h b/src/juce_app_includes.h index 2fa80fa376..cd1e3c9ff1 100644 --- a/src/juce_app_includes.h +++ b/src/juce_app_includes.h @@ -185,12 +185,6 @@ #ifndef __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ #include "audio/audio_file_formats/juce_AiffAudioFormat.h" #endif -#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ - #include "audio/audio_file_formats/juce_AudioCDBurner.h" -#endif -#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ - #include "audio/audio_file_formats/juce_AudioCDReader.h" -#endif #ifndef __JUCE_AUDIOFORMAT_JUCEHEADER__ #include "audio/audio_file_formats/juce_AudioFormat.h" #endif @@ -215,14 +209,20 @@ #ifndef __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ #include "audio/audio_file_formats/juce_FlacAudioFormat.h" #endif +#ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ + #include "audio/audio_file_formats/juce_WavAudioFormat.h" +#endif +#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ + #include "audio/audio_file_formats/juce_AudioCDReader.h" +#endif #ifndef __JUCE_OGGVORBISAUDIOFORMAT_JUCEHEADER__ #include "audio/audio_file_formats/juce_OggVorbisAudioFormat.h" #endif #ifndef __JUCE_QUICKTIMEAUDIOFORMAT_JUCEHEADER__ #include "audio/audio_file_formats/juce_QuickTimeAudioFormat.h" #endif -#ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ - #include "audio/audio_file_formats/juce_WavAudioFormat.h" +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ + #include "audio/audio_file_formats/juce_AudioCDBurner.h" #endif #ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__ #include "events/juce_ActionBroadcaster.h" @@ -269,21 +269,6 @@ #ifndef __JUCE_TIMER_JUCEHEADER__ #include "events/juce_Timer.h" #endif -#ifndef __JUCE_BRUSH_JUCEHEADER__ - #include "gui/graphics/brushes/juce_Brush.h" -#endif -#ifndef __JUCE_GRADIENTBRUSH_JUCEHEADER__ - #include "gui/graphics/brushes/juce_GradientBrush.h" -#endif -#ifndef __JUCE_IMAGEBRUSH_JUCEHEADER__ - #include "gui/graphics/brushes/juce_ImageBrush.h" -#endif -#ifndef __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ - #include "gui/graphics/brushes/juce_SolidColourBrush.h" -#endif -#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ - #include "gui/graphics/colour/juce_PixelFormats.h" -#endif #ifndef __JUCE_COLOUR_JUCEHEADER__ #include "gui/graphics/colour/juce_Colour.h" #endif @@ -293,41 +278,44 @@ #ifndef __JUCE_COLOURGRADIENT_JUCEHEADER__ #include "gui/graphics/colour/juce_ColourGradient.h" #endif -#ifndef __JUCE_FONT_JUCEHEADER__ - #include "gui/graphics/fonts/juce_Font.h" -#endif -#ifndef __JUCE_TEXTLAYOUT_JUCEHEADER__ - #include "gui/graphics/fonts/juce_TextLayout.h" +#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ + #include "gui/graphics/colour/juce_PixelFormats.h" #endif #ifndef __JUCE_TYPEFACE_JUCEHEADER__ #include "gui/graphics/fonts/juce_Typeface.h" #endif +#ifndef __JUCE_TEXTLAYOUT_JUCEHEADER__ + #include "gui/graphics/fonts/juce_TextLayout.h" +#endif +#ifndef __JUCE_FONT_JUCEHEADER__ + #include "gui/graphics/fonts/juce_Font.h" +#endif #ifndef __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ #include "gui/graphics/fonts/juce_GlyphArrangement.h" #endif -#ifndef __JUCE_EDGETABLE_JUCEHEADER__ - #include "gui/graphics/contexts/juce_EdgeTable.h" +#ifndef __JUCE_GRAPHICS_JUCEHEADER__ + #include "gui/graphics/contexts/juce_Graphics.h" #endif #ifndef __JUCE_JUSTIFICATION_JUCEHEADER__ #include "gui/graphics/contexts/juce_Justification.h" #endif +#ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ + #include "gui/graphics/contexts/juce_RectanglePlacement.h" +#endif +#ifndef __JUCE_EDGETABLE_JUCEHEADER__ + #include "gui/graphics/contexts/juce_EdgeTable.h" +#endif #ifndef __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ #include "gui/graphics/contexts/juce_LowLevelGraphicsContext.h" #endif #ifndef __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ #include "gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" #endif -#ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ - #include "gui/graphics/contexts/juce_RectanglePlacement.h" -#endif -#ifndef __JUCE_GRAPHICS_JUCEHEADER__ - #include "gui/graphics/contexts/juce_Graphics.h" -#endif #ifndef __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ #include "gui/graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h" #endif -#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__ - #include "gui/graphics/geometry/juce_AffineTransform.h" +#ifndef __JUCE_PATH_JUCEHEADER__ + #include "gui/graphics/geometry/juce_Path.h" #endif #ifndef __JUCE_BORDERSIZE_JUCEHEADER__ #include "gui/graphics/geometry/juce_BorderSize.h" @@ -335,36 +323,36 @@ #ifndef __JUCE_LINE_JUCEHEADER__ #include "gui/graphics/geometry/juce_Line.h" #endif -#ifndef __JUCE_PATH_JUCEHEADER__ - #include "gui/graphics/geometry/juce_Path.h" -#endif -#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ - #include "gui/graphics/geometry/juce_PathIterator.h" -#endif -#ifndef __JUCE_PATHSTROKETYPE_JUCEHEADER__ - #include "gui/graphics/geometry/juce_PathStrokeType.h" -#endif #ifndef __JUCE_POINT_JUCEHEADER__ #include "gui/graphics/geometry/juce_Point.h" #endif -#ifndef __JUCE_POSITIONEDRECTANGLE_JUCEHEADER__ - #include "gui/graphics/geometry/juce_PositionedRectangle.h" -#endif #ifndef __JUCE_RECTANGLE_JUCEHEADER__ #include "gui/graphics/geometry/juce_Rectangle.h" #endif +#ifndef __JUCE_PATHSTROKETYPE_JUCEHEADER__ + #include "gui/graphics/geometry/juce_PathStrokeType.h" +#endif +#ifndef __JUCE_POSITIONEDRECTANGLE_JUCEHEADER__ + #include "gui/graphics/geometry/juce_PositionedRectangle.h" +#endif #ifndef __JUCE_RECTANGLELIST_JUCEHEADER__ #include "gui/graphics/geometry/juce_RectangleList.h" #endif +#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ + #include "gui/graphics/geometry/juce_PathIterator.h" +#endif +#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__ + #include "gui/graphics/geometry/juce_AffineTransform.h" +#endif #ifndef __JUCE_CAMERADEVICE_JUCEHEADER__ #include "gui/graphics/imaging/juce_CameraDevice.h" #endif -#ifndef __JUCE_IMAGE_JUCEHEADER__ - #include "gui/graphics/imaging/juce_Image.h" -#endif #ifndef __JUCE_IMAGECACHE_JUCEHEADER__ #include "gui/graphics/imaging/juce_ImageCache.h" #endif +#ifndef __JUCE_IMAGE_JUCEHEADER__ + #include "gui/graphics/imaging/juce_Image.h" +#endif #ifndef __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ #include "gui/graphics/imaging/juce_ImageFileFormat.h" #endif @@ -380,12 +368,12 @@ #ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ #include "gui/graphics/drawables/juce_DrawableImage.h" #endif -#ifndef __JUCE_DRAWABLEPATH_JUCEHEADER__ - #include "gui/graphics/drawables/juce_DrawablePath.h" -#endif #ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ #include "gui/graphics/drawables/juce_DrawableText.h" #endif +#ifndef __JUCE_DRAWABLEPATH_JUCEHEADER__ + #include "gui/graphics/drawables/juce_DrawablePath.h" +#endif #ifndef __JUCE_COMPONENT_JUCEHEADER__ #include "gui/components/juce_Component.h" #endif @@ -458,12 +446,12 @@ #ifndef __JUCE_CODEEDITORCOMPONENT_JUCEHEADER__ #include "gui/components/code_editor/juce_CodeEditorComponent.h" #endif -#ifndef __JUCE_CODEDOCUMENT_JUCEHEADER__ - #include "gui/components/code_editor/juce_CodeDocument.h" -#endif #ifndef __JUCE_CPLUSPLUSCODETOKENISER_JUCEHEADER__ #include "gui/components/code_editor/juce_CPlusPlusCodeTokeniser.h" #endif +#ifndef __JUCE_CODEDOCUMENT_JUCEHEADER__ + #include "gui/components/code_editor/juce_CodeDocument.h" +#endif #ifndef __JUCE_CODETOKENISER_JUCEHEADER__ #include "gui/components/code_editor/juce_CodeTokeniser.h" #endif @@ -545,12 +533,12 @@ #ifndef __JUCE_TOOLBARITEMPALETTE_JUCEHEADER__ #include "gui/components/controls/juce_ToolbarItemPalette.h" #endif -#ifndef __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ - #include "gui/components/controls/juce_ToolbarItemComponent.h" -#endif #ifndef __JUCE_TREEVIEW_JUCEHEADER__ #include "gui/components/controls/juce_TreeView.h" #endif +#ifndef __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ + #include "gui/components/controls/juce_ToolbarItemComponent.h" +#endif #ifndef __JUCE_BOOLEANPROPERTYCOMPONENT_JUCEHEADER__ #include "gui/components/properties/juce_BooleanPropertyComponent.h" #endif @@ -560,12 +548,12 @@ #ifndef __JUCE_CHOICEPROPERTYCOMPONENT_JUCEHEADER__ #include "gui/components/properties/juce_ChoicePropertyComponent.h" #endif -#ifndef __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ - #include "gui/components/properties/juce_PropertyComponent.h" -#endif #ifndef __JUCE_PROPERTYPANEL_JUCEHEADER__ #include "gui/components/properties/juce_PropertyPanel.h" #endif +#ifndef __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ + #include "gui/components/properties/juce_PropertyComponent.h" +#endif #ifndef __JUCE_SLIDERPROPERTYCOMPONENT_JUCEHEADER__ #include "gui/components/properties/juce_SliderPropertyComponent.h" #endif @@ -629,33 +617,33 @@ #ifndef __JUCE_FILECHOOSER_JUCEHEADER__ #include "gui/components/filebrowser/juce_FileChooser.h" #endif -#ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ - #include "gui/components/filebrowser/juce_FileChooserDialogBox.h" -#endif #ifndef __JUCE_FILEFILTER_JUCEHEADER__ #include "gui/components/filebrowser/juce_FileFilter.h" #endif +#ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ + #include "gui/components/filebrowser/juce_FileChooserDialogBox.h" +#endif #ifndef __JUCE_FILELISTCOMPONENT_JUCEHEADER__ #include "gui/components/filebrowser/juce_FileListComponent.h" #endif #ifndef __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ #include "gui/components/filebrowser/juce_FilePreviewComponent.h" #endif -#ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ - #include "gui/components/filebrowser/juce_FileSearchPathListComponent.h" -#endif #ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ #include "gui/components/filebrowser/juce_FileTreeComponent.h" #endif +#ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ + #include "gui/components/filebrowser/juce_FileSearchPathListComponent.h" +#endif #ifndef __JUCE_FILENAMECOMPONENT_JUCEHEADER__ #include "gui/components/filebrowser/juce_FilenameComponent.h" #endif -#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ - #include "gui/components/filebrowser/juce_ImagePreviewComponent.h" -#endif #ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ #include "gui/components/filebrowser/juce_WildcardFileFilter.h" #endif +#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ + #include "gui/components/filebrowser/juce_ImagePreviewComponent.h" +#endif #ifndef __JUCE_ALERTWINDOW_JUCEHEADER__ #include "gui/components/windows/juce_AlertWindow.h" #endif @@ -716,12 +704,12 @@ #ifndef __JUCE_PREFERENCESPANEL_JUCEHEADER__ #include "gui/components/special/juce_PreferencesPanel.h" #endif -#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ - #include "gui/components/special/juce_SystemTrayIconComponent.h" -#endif #ifndef __JUCE_WEBBROWSERCOMPONENT_JUCEHEADER__ #include "gui/components/special/juce_WebBrowserComponent.h" #endif +#ifndef __JUCE_SYSTEMTRAYICONCOMPONENT_JUCEHEADER__ + #include "gui/components/special/juce_SystemTrayIconComponent.h" +#endif #ifndef __JUCE_QUICKTIMEMOVIECOMPONENT_JUCEHEADER__ #include "gui/components/special/juce_QuickTimeMovieComponent.h" #endif diff --git a/src/juce_core_includes.h b/src/juce_core_includes.h index 886fc84cb7..b560b56126 100644 --- a/src/juce_core_includes.h +++ b/src/juce_core_includes.h @@ -41,8 +41,8 @@ #ifndef __JUCE_LOGGER_JUCEHEADER__ #include "core/juce_Logger.h" #endif -#ifndef __JUCE_MATHSFUNCTIONS_JUCEHEADER__ - #include "core/juce_MathsFunctions.h" +#ifndef __JUCE_PLATFORMUTILITIES_JUCEHEADER__ + #include "core/juce_PlatformUtilities.h" #endif #ifndef __JUCE_MEMORY_JUCEHEADER__ #include "core/juce_Memory.h" @@ -53,8 +53,8 @@ #ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ #include "core/juce_PlatformDefs.h" #endif -#ifndef __JUCE_PLATFORMUTILITIES_JUCEHEADER__ - #include "core/juce_PlatformUtilities.h" +#ifndef __JUCE_SINGLETON_JUCEHEADER__ + #include "core/juce_Singleton.h" #endif #ifndef __JUCE_RANDOM_JUCEHEADER__ #include "core/juce_Random.h" @@ -62,24 +62,24 @@ #ifndef __JUCE_RELATIVETIME_JUCEHEADER__ #include "core/juce_RelativeTime.h" #endif -#ifndef __JUCE_SINGLETON_JUCEHEADER__ - #include "core/juce_Singleton.h" -#endif -#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ - #include "core/juce_StandardHeader.h" -#endif #ifndef __JUCE_SYSTEMSTATS_JUCEHEADER__ #include "core/juce_SystemStats.h" #endif -#ifndef __JUCE_TARGETPLATFORM_JUCEHEADER__ - #include "core/juce_TargetPlatform.h" -#endif #ifndef __JUCE_TIME_JUCEHEADER__ #include "core/juce_Time.h" #endif #ifndef __JUCE_UUID_JUCEHEADER__ #include "core/juce_Uuid.h" #endif +#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ + #include "core/juce_StandardHeader.h" +#endif +#ifndef __JUCE_MATHSFUNCTIONS_JUCEHEADER__ + #include "core/juce_MathsFunctions.h" +#endif +#ifndef __JUCE_TARGETPLATFORM_JUCEHEADER__ + #include "core/juce_TargetPlatform.h" +#endif #ifndef __JUCE_ARRAY_JUCEHEADER__ #include "containers/juce_Array.h" #endif @@ -236,14 +236,14 @@ #ifndef __JUCE_THREAD_JUCEHEADER__ #include "threads/juce_Thread.h" #endif -#ifndef __JUCE_THREADPOOL_JUCEHEADER__ - #include "threads/juce_ThreadPool.h" -#endif #ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ #include "threads/juce_TimeSliceThread.h" #endif #ifndef __JUCE_WAITABLEEVENT_JUCEHEADER__ #include "threads/juce_WaitableEvent.h" #endif +#ifndef __JUCE_THREADPOOL_JUCEHEADER__ + #include "threads/juce_ThreadPool.h" +#endif #endif diff --git a/src/native/mac/juce_mac_CoreGraphicsContext.mm b/src/native/mac/juce_mac_CoreGraphicsContext.mm index ea489b035f..57fcaf573c 100644 --- a/src/native/mac/juce_mac_CoreGraphicsContext.mm +++ b/src/native/mac/juce_mac_CoreGraphicsContext.mm @@ -73,7 +73,9 @@ class CoreGraphicsContext : public LowLevelGraphicsContext public: CoreGraphicsContext (CGContextRef context_, const float flipHeight_) : context (context_), - flipHeight (flipHeight_) + flipHeight (flipHeight_), + gradientLookupTable (0), + numGradientLookupEntries (0) { CGContextRetain (context); CGContextSetShouldSmoothFonts (context, true); @@ -93,6 +95,7 @@ public: CGColorSpaceRelease (rgbColourSpace); CGColorSpaceRelease (greyColourSpace); delete state; + delete gradientLookupTable; } //============================================================================== @@ -103,13 +106,13 @@ public: CGContextTranslateCTM (context, x, -y); } - bool reduceClipRegion (int x, int y, int w, int h) + bool clipToRectangle (const Rectangle& r) { - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextClipToRect (context, CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight())); return ! isClipEmpty(); } - bool reduceClipRegion (const RectangleList& clipRegion) + bool clipToRectangleList (const RectangleList& clipRegion) { const int numRects = clipRegion.getNumRectangles(); CGRect* const rects = new CGRect [numRects]; @@ -125,13 +128,62 @@ public: return ! isClipEmpty(); } - void excludeClipRegion (int x, int y, int w, int h) + void excludeClipRectangle (const Rectangle& r) { - RectangleList r (getClipBounds()); - r.subtract (Rectangle (x, y, w, h)); - reduceClipRegion (r); + RectangleList remaining (getClipBounds()); + remaining.subtract (r); + clipToRectangleList (remaining); } + void clipToPath (const Path& path, const AffineTransform& transform) + { + createPath (path, transform); + CGContextClip (context); + } + + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) + { + if (! transform.isSingularity()) + { + Image* singleChannelImage = createAlphaChannelImage (sourceImage); + CGImageRef image = createImage (*singleChannelImage, true); + + flip(); + AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + applyTransform (t); + + CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + CGContextClipToMask (context, r, image); + + applyTransform (t.inverted()); + flip(); + + CGImageRelease (image); + deleteAlphaChannelImage (sourceImage, singleChannelImage); + } + } + + bool clipRegionIntersects (const Rectangle& r) + { + return getClipBounds().intersects (r); + } + + const Rectangle getClipBounds() const + { + CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); + + return Rectangle (roundFloatToInt (bounds.origin.x), + roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)), + roundFloatToInt (bounds.size.width), + roundFloatToInt (bounds.size.height)); + } + + bool isClipEmpty() const + { + return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); + } + + //============================================================================== void saveState() { CGContextSaveGState (context); @@ -156,31 +208,10 @@ public: } } - bool clipRegionIntersects (int x, int y, int w, int h) - { - return getClipBounds().intersects (Rectangle (x, y, w, h)); - } - - const Rectangle getClipBounds() const - { - CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); - - return Rectangle (roundFloatToInt (bounds.origin.x), - roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)), - roundFloatToInt (bounds.size.width), - roundFloatToInt (bounds.size.height)); - } - - bool isClipEmpty() const - { - return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); - } - //============================================================================== void setColour (const Colour& colour) { - state->colour = colour; - deleteAndZero (state->gradient); + state->fillType.setColour (colour); CGContextSetRGBFillColor (context, colour.getFloatRed(), colour.getFloatGreen(), @@ -190,15 +221,17 @@ public: void setGradient (const ColourGradient& gradient) { - if (state->gradient == 0) - state->gradient = new ColourGradient (gradient); - else - *state->gradient = gradient; + state->fillType.setGradient (gradient); + } + + void setTiledFill (const Image& image, int x, int y) + { + state->fillType.setTiledImage (image, x, y); } void setOpacity (float opacity) { - setColour (state->colour.withAlpha (opacity)); + state->fillType.colour = state->fillType.colour.withAlpha (opacity); } void setInterpolationQuality (Graphics::ResamplingQuality quality) @@ -209,36 +242,46 @@ public: } //============================================================================== - void fillRect (int x, int y, int w, int h, const bool replaceExistingContents) + void fillRect (const Rectangle& r, const bool replaceExistingContents) { + CGRect cgRect = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()); + if (replaceExistingContents) { #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 - CGContextClearRect (context, CGRectMake (x, y, w, h)); + CGContextClearRect (context, cgRect); #else #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 if (CGContextDrawLinearGradient == 0) // (just a way of checking whether we're running in 10.5 or later) - CGContextClearRect (context, CGRectMake (x, y, w, h)); + CGContextClearRect (context, cgRect); else #endif CGContextSetBlendMode (context, kCGBlendModeCopy); #endif - fillRect (x, y, w, h, false); + fillRect (r, false); CGContextSetBlendMode (context, kCGBlendModeNormal); } else { - if (state->gradient == 0) + if (state->fillType.isColour()) { - CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextFillRect (context, cgRect); + } + else if (state->fillType.isGradient()) + { + CGContextSaveGState (context); + CGContextClipToRect (context, cgRect); + flip(); + drawGradient(); + CGContextRestoreGState (context); } else { CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); - flip(); - drawGradient(); + CGContextClipToRect (context, cgRect); + drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), + AffineTransform::translation (state->fillType.imageX, state->fillType.imageY), true); CGContextRestoreGState (context); } } @@ -248,7 +291,7 @@ public: { CGContextSaveGState (context); - if (state->gradient == 0) + if (state->fillType.isColour()) { flip(); applyTransform (transform); @@ -259,102 +302,78 @@ public: else CGContextEOFillPath (context); } - else + else if (state->fillType.isGradient()) { createPath (path, transform); CGContextClip (context); flip(); - applyTransform (state->gradient->transform); + applyTransform (state->fillType.gradient->transform); drawGradient(); } + else + { + createPath (path, transform); + CGContextClip (context); + drawImage (*(state->fillType.image), Rectangle (0, 0, state->fillType.image->getWidth(), state->fillType.image->getHeight()), + AffineTransform::translation (state->fillType.imageX, state->fillType.imageY), true); + } + CGContextRestoreGState (context); } - void fillPathWithImage (const Path& path, const AffineTransform& transform, - const Image& image, int imageX, int imageY) - { - CGContextSaveGState (context); - createPath (path, transform); - CGContextClip (context); - blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0); - CGContextRestoreGState (context); - } - - void fillAlphaChannel (const Image& alphaImage, int alphaImageX, int alphaImageY) - { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); - - CGContextSaveGState (context); - CGContextSetAlpha (context, 1.0f); - - CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), - alphaImage.getWidth(), alphaImage.getHeight()); - CGContextClipToMask (context, r, image); - - fillRect (alphaImageX, alphaImageY, alphaImage.getWidth(), alphaImage.getHeight(), false); - - CGContextRestoreGState (context); - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } - - void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, - const Image& fillerImage, int fillerImageX, int fillerImageY) - { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); - - CGContextSaveGState (context); - CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), - alphaImage.getWidth(), alphaImage.getHeight()); - CGContextClipToMask (context, r, image); - - blendImage (fillerImage, fillerImageX, fillerImageY, - fillerImage.getWidth(), fillerImage.getHeight(), - 0, 0); - - CGContextRestoreGState (context); - - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } - - //============================================================================== - void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY) - { - CGImageRef image = createImage (sourceImage, false); - - CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (destX, flipHeight - (destY + destH), destW, destH)); - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (destX - sourceX, - flipHeight - ((destY - sourceY) + sourceImage.getHeight()), - sourceImage.getWidth(), - sourceImage.getHeight()), image); - - CGContextRestoreGState (context); - CGImageRelease (image); - } - - void blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) + void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { CGImageRef fullImage = createImage (sourceImage, false); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), - srcClipW, srcClipH)); + CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); CGImageRelease (fullImage); CGContextSaveGState (context); + CGContextSetAlpha (context, state->fillType.colour.getFloatAlpha()); + flip(); applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + CGRect imageRect = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), - sourceImage.getHeight()), image); + if (fillEntireClipAsTiles) + { +#if JUCE_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + CGContextDrawTiledImage (context, imageRect, image); +#else + #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (CGContextDrawTiledImage != 0) + CGContextDrawTiledImage (context, imageRect, image); + else + #endif + { + // Fallback to manually doing a tiled fill on 10.4 + CGRect clip = CGRectIntegral (CGContextGetClipBoundingBox (context)); + const int iw = sourceImage.getWidth(); + const int ih = sourceImage.getHeight(); + + int x = 0, y = 0; + while (x > clip.origin.x) x -= iw; + while (y > clip.origin.y) y -= ih; + + const int right = clip.origin.x + clip.size.width; + const int bottom = clip.origin.y + clip.size.height; + + while (y < bottom) + { + for (int x2 = x; x2 < right; x2 += iw) + CGContextDrawImage (context, CGRectMake (x2, y, iw, ih), image); + + y += ih; + } + } +#endif + } + else + { + CGContextDrawImage (context, imageRect, image); + } CGImageRelease (image); CGContextRestoreGState (context); @@ -366,8 +385,8 @@ public: CGContextSetLineCap (context, kCGLineCapSquare); CGContextSetLineWidth (context, 1.0f); CGContextSetRGBStrokeColor (context, - state->colour.getFloatRed(), state->colour.getFloatGreen(), - state->colour.getFloatBlue(), state->colour.getFloatAlpha()); + state->fillType.colour.getFloatRed(), state->fillType.colour.getFloatGreen(), + state->fillType.colour.getFloatBlue(), state->fillType.colour.getFloatAlpha()); CGPoint line[] = { { x1 + 0.5f, flipHeight - (y1 + 0.5f) }, { x2 + 0.5f, flipHeight - (y2 + 0.5f) } }; @@ -407,40 +426,46 @@ public: } } - void drawGlyph (int glyphNumber, float x, float y) + const Font getFont() { - if (state->fontRef != 0 && state->gradient == 0) - { - CGGlyph g = glyphNumber; - CGContextShowGlyphsAtPoint (context, x, flipHeight - roundFloatToInt (y), &g, 1); - } - else - { - state->font.renderGlyphIndirectly (*this, glyphNumber, x, y); - } + return state->font; } void drawGlyph (int glyphNumber, const AffineTransform& transform) { - if (state->fontRef != 0) + if (state->fontRef != 0 && state->fillType.isColour()) { - CGContextSaveGState (context); - flip(); - applyTransform (transform); + if (transform.isOnlyTranslation()) + { + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, transform.getTranslationX(), + flipHeight - roundFloatToInt (transform.getTranslationY()), &g, 1); + } + else + { + CGContextSaveGState (context); + flip(); + applyTransform (transform); - CGAffineTransform t = state->fontTransform; - t.d = -t.d; - CGContextSetTextMatrix (context, t); + CGAffineTransform t = state->fontTransform; + t.d = -t.d; + CGContextSetTextMatrix (context, t); - CGGlyph g = glyphNumber; - CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); + CGGlyph g = glyphNumber; + CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); - CGContextSetTextMatrix (context, state->fontTransform); - CGContextRestoreGState (context); + CGContextSetTextMatrix (context, state->fontTransform); + CGContextRestoreGState (context); + } } else { - state->font.renderGlyphIndirectly (*this, glyphNumber, transform); + Path p; + Font& f = state->font; + f.getTypeface()->getOutlineForGlyph (glyphNumber, p); + + fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight()) + .followedBy (transform)); } } @@ -453,26 +478,21 @@ private: struct SavedState { SavedState() throw() - : gradient (0), font (1.0f), fontRef (0), - fontTransform (CGAffineTransformIdentity) + : font (1.0f), fontRef (0), fontTransform (CGAffineTransformIdentity) { } SavedState (const SavedState& other) throw() - : colour (other.colour), - gradient (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0), - font (other.font), fontRef (other.fontRef), + : fillType (other.fillType), font (other.font), fontRef (other.fontRef), fontTransform (other.fontTransform) { } ~SavedState() throw() { - delete gradient; } - Colour colour; - ColourGradient* gradient; + Graphics::FillType fillType; Font font; CGFontRef fontRef; CGAffineTransform fontTransform; @@ -480,21 +500,31 @@ private: SavedState* state; OwnedArray stateStack; + PixelARGB* gradientLookupTable; + int numGradientLookupEntries; static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) { - const ColourGradient* const g = (const ColourGradient*) info; - const Colour c (g->getColourAtPosition (inData[0])); - outData[0] = c.getFloatRed(); - outData[1] = c.getFloatGreen(); - outData[2] = c.getFloatBlue(); - outData[3] = c.getFloatAlpha(); + const CoreGraphicsContext* const g = (const CoreGraphicsContext*) info; + + const int index = roundFloatToInt (g->numGradientLookupEntries * inData[0]); + PixelARGB colour (g->gradientLookupTable [jlimit (0, g->numGradientLookupEntries, index)]); + colour.unpremultiply(); + + outData[0] = colour.getRed() / 255.0f; + outData[1] = colour.getGreen() / 255.0f; + outData[2] = colour.getBlue() / 255.0f; + outData[3] = colour.getAlpha() / 255.0f; } - CGShadingRef createGradient (const ColourGradient* const gradient) const throw() + CGShadingRef createGradient (const ColourGradient* const gradient) throw() { + delete gradientLookupTable; + gradientLookupTable = gradient->createLookupTable (numGradientLookupEntries); + --numGradientLookupEntries; + CGShadingRef result = 0; - CGFunctionRef function = CGFunctionCreate ((void*) gradient, 1, 0, 4, 0, &gradientCallbacks); + CGFunctionRef function = CGFunctionCreate ((void*) this, 1, 0, 4, 0, &gradientCallbacks); CGPoint p1 (CGPointMake (gradient->x1, gradient->y1)); if (gradient->isRadial) @@ -514,12 +544,12 @@ private: return result; } - void drawGradient() const throw() + void drawGradient() throw() { CGContextSetAlpha (context, 1.0f); CGContextSetInterpolationQuality (context, kCGInterpolationDefault); // (This is required for 10.4, where there's a crash if // you draw a gradient with high quality interp enabled). - CGShadingRef shading = createGradient (state->gradient); + CGShadingRef shading = createGradient (state->fillType.gradient); CGContextDrawShading (context, shading); CGShadingRelease (shading); } diff --git a/src/native/mac/juce_mac_MessageManager.mm b/src/native/mac/juce_mac_MessageManager.mm index 381bcd98b0..a1956628c7 100644 --- a/src/native/mac/juce_mac_MessageManager.mm +++ b/src/native/mac/juce_mac_MessageManager.mm @@ -409,7 +409,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) if (e != 0 && ! isEventBlockedByModalComps (e)) [NSApp sendEvent: e]; - + if (Time::getMillisecondCounter() >= endTime) break; } diff --git a/src/native/mac/juce_mac_NSViewComponentPeer.mm b/src/native/mac/juce_mac_NSViewComponentPeer.mm index ee86088955..f84c186771 100644 --- a/src/native/mac/juce_mac_NSViewComponentPeer.mm +++ b/src/native/mac/juce_mac_NSViewComponentPeer.mm @@ -1471,7 +1471,7 @@ void NSViewComponentPeer::drawRect (NSRect r) roundFloatToInt (rects[i].size.height))); } - if (context.reduceClipRegion (clip)) + if (context.clipToRectangleList (clip)) { insideDrawRect = true; handlePaint (context); diff --git a/src/native/mac/juce_mac_NativeIncludes.h b/src/native/mac/juce_mac_NativeIncludes.h index ec0bfd25ac..19fd3ed9b0 100644 --- a/src/native/mac/juce_mac_NativeIncludes.h +++ b/src/native/mac/juce_mac_NativeIncludes.h @@ -35,7 +35,7 @@ #include "../../core/juce_StandardHeader.h" -#define USE_COREGRAPHICS_RENDERING 0 +#define USE_COREGRAPHICS_RENDERING 1 #if JUCE_IPHONE #import diff --git a/src/native/windows/juce_win32_Windowing.cpp b/src/native/windows/juce_win32_Windowing.cpp index 4d166d4953..0a8be4c405 100644 --- a/src/native/windows/juce_win32_Windowing.cpp +++ b/src/native/windows/juce_win32_Windowing.cpp @@ -1253,7 +1253,7 @@ private: updateCurrentModifiers(); LowLevelGraphicsSoftwareRenderer context (*offscreenImage); - context.reduceClipRegion (contextClip); + context.clipToRectangleList (contextClip); context.setOrigin (-x, -y); handlePaint (context);