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()) - { - 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()); + if (y < 0 || y >= bounds.getHeight()) + return; - for (int i = top; --i >= 0;) - table [lineStrideElements * i] = 0; + needToCheckEmptinesss = true; - int imageX = clipped.getX() - x; - int imageY = clipped.getY() - y; + if (numPixels <= 0) + { + table [lineStrideElements * y] = 0; + return; + } - int* temp = (int*) alloca ((clipped.getWidth() * 2 + 4) * sizeof (int)); + int* tempLine = (int*) alloca ((numPixels * 2 + 4) * sizeof (int)); + int destIndex = 0, lastLevel = 0; - Image::BitmapData srcData (image, imageX, imageY, clipped.getWidth(), clipped.getHeight()); + while (--numPixels >= 0) + { + const int alpha = *mask; + mask += maskStride; - for (int i = 0; i < clipped.getHeight(); ++i) + if (alpha != lastLevel) { - 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; + tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = alpha; + lastLevel = alpha; + } - if (alpha != lastLevel) - { - temp[++destIndex] = (clipped.getX() + px) << 8; - temp[++destIndex] = alpha; - lastLevel = alpha; - } - } - } + ++x; + } - if (lastLevel > 0) - { - temp[++destIndex] = clipped.getRight() << 8; - temp[++destIndex] = 0; - } + if (lastLevel > 0) + { + tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = 0; + } - temp[0] = destIndex >> 1; + tempLine[0] = destIndex >> 1; - intersectWithEdgeTableLine (top + i, temp); - ++y; - } - } + intersectWithEdgeTableLine (y, tempLine); } -bool EdgeTable::isEmpty() const throw() +bool EdgeTable::isEmpty() throw() { - int* t = table; - - for (int i = bounds.getHeight(); --i >= 0;) + if (needToCheckEmptinesss) { - if (t[0] > 1) - return false; + needToCheckEmptinesss = false; + int* t = table; - t += lineStrideElements; + for (int i = bounds.getHeight(); --i >= 0;) + { + if (t[0] > 1) + return false; + + t += lineStrideElements; + } + + bounds.setHeight (0); } - 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,201 +79836,113 @@ 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; - } + 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); +} - if (dw <= 0 || dh <= 0 || sw <= 0 || sh <= 0) - return; +void Graphics::drawImageTransformed (const Image* const imageToDraw, + int sourceClipX, + int sourceClipY, + int sourceClipWidth, + int sourceClipHeight, + const AffineTransform& transform, + const bool fillAlphaChannelWithCurrentBrush) const throw() +{ + if (imageToDraw != 0 && ! context->isClipEmpty()) + { + const Rectangle srcClip (Rectangle (sourceClipX, sourceClipY, sourceClipWidth, sourceClipHeight)); 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); - } + context->saveState(); + context->clipToImageAlpha (*imageToDraw, srcClip, transform); + fillAll(); + context->restoreState(); } else { - context->blendImage (*imageToDraw, - dx, dy, dw, dh, sx, sy); + context->drawImage (*imageToDraw, srcClip, transform, false); } } - 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; +Graphics::FillType::FillType() throw() + : colour (0xff000000), gradient (0), + image (0), imageX (0), imageY (0) +{ +} - 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); +Graphics::FillType::FillType (const Colour& colour_) throw() + : colour (colour_), gradient (0), + image (0), imageX (0), imageY (0) +{ +} - if (state->brush == 0) - { - context->saveState(); +Graphics::FillType::FillType (const ColourGradient& gradient) throw() + : colour (0xff000000), gradient (new ColourGradient (gradient)), + image (0), imageX (0), imageY (0) +{ +} - if (context->reduceClipRegion (tx, ty, tw, th)) - context->fillAlphaChannel (temp, tx, ty); +Graphics::FillType::FillType (Image* const image_, const int x, const int y) throw() + : colour (0xff000000), gradient (0), + image (image_), imageX (x), imageY (y) +{ +} - 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)); - } - } +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) +{ } -void Graphics::drawImageTransformed (const Image* const imageToDraw, - int sourceClipX, - int sourceClipY, - int sourceClipWidth, - int sourceClipHeight, - const AffineTransform& transform, - const bool fillAlphaChannelWithCurrentBrush) const throw() +const Graphics::FillType& Graphics::FillType::operator= (const FillType& other) throw() { - if (imageToDraw != 0 - && (! context->isClipEmpty()) - && ! transform.isSingularity()) + if (this != &other) { - if (transform.isIdentity()) - { - 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; + colour = other.colour; + delete gradient; + gradient = (other.gradient != 0 ? new ColourGradient (*other.gradient) : 0); + image = other.image; + imageX = other.imageX; + imageY = other.imageY; + } - if (context->getClipBounds().intersectRectangle (tx, ty, tw, th)) - { - Image temp (imageToDraw->getFormat(), tw, th, true); - Graphics g (temp); -//xxx g.setImageResamplingQuality (state->quality); + return *this; +} - g.drawImageTransformed (imageToDraw, - sourceClipX, - sourceClipY, - sourceClipWidth, - sourceClipHeight, - transform.translated ((float) -tx, (float) -ty), - false); +Graphics::FillType::~FillType() throw() +{ + delete gradient; +} - if (state->brush == 0) - { - context->saveState(); +void Graphics::FillType::setColour (const Colour& newColour) throw() +{ + deleteAndZero (gradient); + colour = newColour; +} - if (context->reduceClipRegion (tx, ty, tw, th)) - context->fillAlphaChannel (temp, tx, ty); +void Graphics::FillType::setGradient (const ColourGradient& newGradient) throw() +{ + if (gradient != 0) + *gradient = newGradient; + else + gradient = new ColourGradient (newGradient); +} - context->restoreState(); - } - else - { - state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); - } - } - } - else - { - context->blendImageWarping (*imageToDraw, - sourceClipX, - sourceClipY, - sourceClipWidth, - sourceClipHeight, - transform); - } - } +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 @@ -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); - } + 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; } - 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); - } + 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 (fillType.colour.getPixelARGB()); + + switch (image.getFormat()) + { + 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; } - else if (image.getFormat() == Image::SingleChannel) + } + } + + void renderImage (Image& destImage, const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& t, const EdgeTable* const tiledFillClipRegion) throw() + { + 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) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); + + if ((! betterQuality) || ((tx | ty) & 224) == 0) { - jassert (destData.pixelStride == 4); + const Rectangle srcRect (srcClip.translated ((tx + 128) >> 8, (ty + 128) >> 8)); - if (g2.isRadial) + if (tiledFillClipRegion != 0) { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } + blittedRenderImage3 (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, srcRect.getX(), srcRect.getY()); } else { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); + 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; } + } - juce_free (lookupTable); + if (transform.isSingularity()) + return; + + if (tiledFillClipRegion != 0) + { + transformedRenderImage3 (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, transform, betterQuality); } else { - const PixelARGB fillColour (colour.getPixelARGB()); + Path p; + p.addRectangle (srcClip); - if (replaceContents) - { - 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); - } - } + EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); + et.clipToEdgeTable (edgeTable->edgeTable); + + if (! et.isEmpty()) + transformedRenderImage3 (sourceImage, destImage, et, destData, srcData, alpha, transform, betterQuality); } } - void renderImage (Image& destImage, const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t) throw() + void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& transform) throw() { - if (t.isSingularity()) + 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); - const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + EdgeTable& et = edgeTable->edgeTable; 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 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; } } - 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()) + if (transform.isSingularity()) { - 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; + et.clipToRectangle (Rectangle()); + return; + } - default: - jassert (sourceImage.getFormat() == Image::SingleChannel); + { + Path p; + p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); + EdgeTable et2 (et.getMaximumBounds(), p, transform); + et.clipToEdgeTable (et2); + } - 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); - } + if (! et.isEmpty()) + { + if (image.getFormat() == Image::ARGB) + transformedClipImage (et, srcData, transform, betterQuality); else - { - jassert (destImage.getFormat() == Image::SingleChannel) - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - break; + transformedClipImage (et, srcData, transform, betterQuality); } } - void renderImage (Image& destImage, const Image& sourceImage, int imageX, int imageY) throw() + template + void transformedClipImage (EdgeTable& et, const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality) throw() { - EdgeTable et (Rectangle (imageX, imageY, sourceImage.getWidth(), sourceImage.getHeight()) - .getIntersection (Rectangle (0, 0, destImage.getWidth(), destImage.getHeight()))); - et.clipToEdgeTable (edgeTable->edgeTable); + TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); - 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(); + for (int y = 0; y < et.getMaximumBounds().getHeight(); ++y) + renderer.clipEdgeTableLine (et, et.getMaximumBounds().getX(), y + et.getMaximumBounds().getY(), + et.getMaximumBounds().getWidth()); + } - switch (destImage.getFormat()) - { - 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; + 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); - 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; + ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); - 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; - } + 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) -{ - 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) +void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { - 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,40 +261503,44 @@ 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 saveState() + void clipToPath (const Path& path, const AffineTransform& transform) { - CGContextSaveGState (context); - stateStack.add (new SavedState (*state)); + createPath (path, transform); + CGContextClip (context); } - void restoreState() + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) { - CGContextRestoreGState (context); + if (! transform.isSingularity()) + { + Image* singleChannelImage = createAlphaChannelImage (sourceImage); + CGImageRef image = createImage (*singleChannelImage, true); - SavedState* const top = stateStack.getLast(); + flip(); + AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + applyTransform (t); - if (top != 0) - { - delete state; - state = top; - stateStack.removeLast (1, false); - } - else - { - jassertfalse // trying to pop with an empty stack! + CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + CGContextClipToMask (context, r, image); + + applyTransform (t.inverted()); + flip(); + + CGImageRelease (image); + deleteAlphaChannelImage (sourceImage, singleChannelImage); } } - bool clipRegionIntersects (int x, int y, int w, int h) + bool clipRegionIntersects (const Rectangle& r) { - return getClipBounds().intersects (Rectangle (x, y, w, h)); + return getClipBounds().intersects (r); } const Rectangle getClipBounds() const @@ -262377,10 +261558,33 @@ public: return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); } + void saveState() + { + CGContextSaveGState (context); + stateStack.add (new SavedState (*state)); + } + + void restoreState() + { + CGContextRestoreGState (context); + + SavedState* const top = stateStack.getLast(); + + if (top != 0) + { + delete state; + state = top; + stateStack.removeLast (1, false); + } + else + { + jassertfalse // trying to pop with an empty stack! + } + } + 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,38 +261614,48 @@ 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 + else if (state->fillType.isGradient()) { CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextClipToRect (context, cgRect); flip(); drawGradient(); CGContextRestoreGState (context); } + else + { + CGContextSaveGState (context); + 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) + void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); + CGImageRef fullImage = createImage (sourceImage, false); + CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); + CGImageRelease (fullImage); 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); + CGContextSetAlpha (context, state->fillType.colour.getFloatAlpha()); - 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); + flip(); + applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + CGRect imageRect = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - 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); + 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(); - CGContextRestoreGState (context); - CGImageRelease (image); - } + int x = 0, y = 0; + while (x > clip.origin.x) x -= iw; + while (y > clip.origin.y) y -= ih; - void blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) - { - CGImageRef fullImage = createImage (sourceImage, false); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), - srcClipW, srcClipH)); - CGImageRelease (fullImage); + const int right = clip.origin.x + clip.size.width; + const int bottom = clip.origin.y + clip.size.height; - CGContextSaveGState (context); - flip(); - applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + while (y < bottom) + { + for (int x2 = x; x2 < right; x2 += iw) + CGContextDrawImage (context, CGRectMake (x2, y, iw, ih), image); - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), - sourceImage.getHeight()), 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,40 +265958,44 @@ 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 saveState() + void clipToPath (const Path& path, const AffineTransform& transform) { - CGContextSaveGState (context); - stateStack.add (new SavedState (*state)); + createPath (path, transform); + CGContextClip (context); } - void restoreState() + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) { - CGContextRestoreGState (context); + if (! transform.isSingularity()) + { + Image* singleChannelImage = createAlphaChannelImage (sourceImage); + CGImageRef image = createImage (*singleChannelImage, true); - SavedState* const top = stateStack.getLast(); + flip(); + AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + applyTransform (t); - if (top != 0) - { - delete state; - state = top; - stateStack.removeLast (1, false); - } - else - { - jassertfalse // trying to pop with an empty stack! + CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + CGContextClipToMask (context, r, image); + + applyTransform (t.inverted()); + flip(); + + CGImageRelease (image); + deleteAlphaChannelImage (sourceImage, singleChannelImage); } } - bool clipRegionIntersects (int x, int y, int w, int h) + bool clipRegionIntersects (const Rectangle& r) { - return getClipBounds().intersects (Rectangle (x, y, w, h)); + return getClipBounds().intersects (r); } const Rectangle getClipBounds() const @@ -266803,10 +266013,33 @@ public: return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); } + void saveState() + { + CGContextSaveGState (context); + stateStack.add (new SavedState (*state)); + } + + void restoreState() + { + CGContextRestoreGState (context); + + SavedState* const top = stateStack.getLast(); + + if (top != 0) + { + delete state; + state = top; + stateStack.removeLast (1, false); + } + else + { + jassertfalse // trying to pop with an empty stack! + } + } + 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,38 +266069,48 @@ 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 + else if (state->fillType.isGradient()) { CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextClipToRect (context, cgRect); flip(); drawGradient(); CGContextRestoreGState (context); } + else + { + CGContextSaveGState (context); + 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) + void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); + CGImageRef fullImage = createImage (sourceImage, false); + CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); + CGImageRelease (fullImage); 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); + CGContextSetAlpha (context, state->fillType.colour.getFloatAlpha()); - 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); + flip(); + applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + CGRect imageRect = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - 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); + 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(); - CGContextRestoreGState (context); - CGImageRelease (image); - } + int x = 0, y = 0; + while (x > clip.origin.x) x -= iw; + while (y > clip.origin.y) y -= ih; - void blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) - { - CGImageRef fullImage = createImage (sourceImage, false); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), - srcClipW, srcClipH)); - CGImageRelease (fullImage); + const int right = clip.origin.x + clip.size.width; + const int bottom = clip.origin.y + clip.size.height; - CGContextSaveGState (context); - flip(); - applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + while (y < bottom) + { + for (int x2 = x; x2 < right; x2 += iw) + CGContextDrawImage (context, CGRectMake (x2, y, iw, ih), image); - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), - sourceImage.getHeight()), 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__ @@ -7471,412 +7379,93 @@ private: /********* End of inlined file: juce_PlatformUtilities.h *********/ #endif -#ifndef __JUCE_RANDOM_JUCEHEADER__ - -/********* Start of inlined file: juce_Random.h *********/ -#ifndef __JUCE_RANDOM_JUCEHEADER__ -#define __JUCE_RANDOM_JUCEHEADER__ - -/********* Start of inlined file: juce_BitArray.h *********/ -#ifndef __JUCE_BITARRAY_JUCEHEADER__ -#define __JUCE_BITARRAY_JUCEHEADER__ - -class MemoryBlock; - -/** - An array of on/off bits, also usable to store large binary integers. - - A BitArray acts like an arbitrarily large integer whose bits can be set or - cleared, and some basic mathematical operations can be done on the number as - a whole. -*/ -class JUCE_API BitArray -{ -public: - - /** Creates an empty BitArray */ - BitArray() throw(); - - /** Creates a BitArray containing an integer value in its low bits. - - The low 32 bits of the array are initialised with this value. - */ - BitArray (const unsigned int value) throw(); - - /** Creates a BitArray containing an integer value in its low bits. - - The low 32 bits of the array are initialised with the absolute value - passed in, and its sign is set to reflect the sign of the number. - */ - BitArray (const int value) throw(); - - /** Creates a BitArray containing an integer value in its low bits. - - The low 64 bits of the array are initialised with the absolute value - passed in, and its sign is set to reflect the sign of the number. - */ - BitArray (int64 value) throw(); - - /** Creates a copy of another BitArray. */ - BitArray (const BitArray& other) throw(); - - /** Destructor. */ - ~BitArray() throw(); - - /** Copies another BitArray onto this one. */ - const BitArray& operator= (const BitArray& other) throw(); - - /** Two arrays are the same if the same bits are set. */ - bool operator== (const BitArray& other) const throw(); - /** Two arrays are the same if the same bits are set. */ - bool operator!= (const BitArray& other) const throw(); - - /** Clears all bits in the BitArray to 0. */ - void clear() throw(); - - /** Clears a particular bit in the array. */ - void clearBit (const int bitNumber) throw(); - - /** Sets a specified bit to 1. - - If the bit number is high, this will grow the array to accomodate it. - */ - void setBit (const int bitNumber) throw(); - - /** Sets or clears a specified bit. */ - void setBit (const int bitNumber, - const bool shouldBeSet) throw(); - - /** Sets a range of bits to be either on or off. - - @param startBit the first bit to change - @param numBits the number of bits to change - @param shouldBeSet whether to turn these bits on or off - */ - void setRange (int startBit, - int numBits, - const bool shouldBeSet) throw(); - - /** Inserts a bit an a given position, shifting up any bits above it. */ - void insertBit (const int bitNumber, - const bool shouldBeSet) throw(); - - /** Returns the value of a specified bit in the array. - - If the index is out-of-range, the result will be false. - */ - bool operator[] (const int bit) const throw(); - - /** Returns true if no bits are set. */ - bool isEmpty() const throw(); - - /** Returns a range of bits in the array as a new BitArray. - - e.g. getBitRangeAsInt (0, 64) would return the lowest 64 bits. - @see getBitRangeAsInt - */ - const BitArray getBitRange (int startBit, int numBits) const throw(); - - /** Returns a range of bits in the array as an integer value. - - e.g. getBitRangeAsInt (0, 32) would return the lowest 32 bits. - - Asking for more than 32 bits isn't allowed (obviously) - for that, use - getBitRange(). - */ - int getBitRangeAsInt (int startBit, int numBits) const throw(); - - /** Sets a range of bits in the array based on an integer value. - - Copies the given integer into the array, starting at startBit, - and only using up to numBits of the available bits. - */ - void setBitRangeAsInt (int startBit, int numBits, - unsigned int valueToSet) throw(); - - /** Performs a bitwise OR with another BitArray. - - The result ends up in this array. - */ - void orWith (const BitArray& other) throw(); - - /** Performs a bitwise AND with another BitArray. - - The result ends up in this array. - */ - void andWith (const BitArray& other) throw(); - - /** Performs a bitwise XOR with another BitArray. - - The result ends up in this array. - */ - void xorWith (const BitArray& other) throw(); - - /** Adds another BitArray's value to this one. - - Treating the two arrays as large positive integers, this - adds them up and puts the result in this array. - */ - void add (const BitArray& other) throw(); - - /** Subtracts another BitArray's value from this one. - - Treating the two arrays as large positive integers, this - subtracts them and puts the result in this array. - - Note that if the result should be negative, this won't be - handled correctly. - */ - void subtract (const BitArray& other) throw(); - - /** Multiplies another BitArray's value with this one. - - Treating the two arrays as large positive integers, this - multiplies them and puts the result in this array. - */ - void multiplyBy (const BitArray& other) throw(); - - /** Divides another BitArray's value into this one and also produces a remainder. - - Treating the two arrays as large positive integers, this - divides this value by the other, leaving the quotient in this - array, and the remainder is copied into the other BitArray passed in. - */ - void divideBy (const BitArray& divisor, BitArray& remainder) throw(); - - /** Returns the largest value that will divide both this value and the one - passed-in. - */ - const BitArray findGreatestCommonDivisor (BitArray other) const throw(); - - /** Performs a modulo operation on this value. - - The result is stored in this value. - */ - void modulo (const BitArray& divisor) throw(); - - /** Performs a combined exponent and modulo operation. - - This BitArray's value becomes (this ^ exponent) % modulus. - */ - void exponentModulo (const BitArray& exponent, const BitArray& modulus) throw(); - - /** Performs an inverse modulo on the value. - - i.e. the result is (this ^ -1) mod (modulus). - */ - void inverseModulo (const BitArray& modulus) throw(); - - /** Shifts a section of bits left or right. - - @param howManyBitsLeft how far to move the bits (+ve numbers shift it left, -ve numbers shift it right). - @param startBit the first bit to affect - if this is > 0, only bits above that index will be affected. - */ - void shiftBits (int howManyBitsLeft, - int startBit = 0) throw(); - - /** Does a signed comparison of two BitArrays. - - Return values are: - - 0 if the numbers are the same - - < 0 if this number is smaller than the other - - > 0 if this number is bigger than the other - */ - int compare (const BitArray& other) const throw(); - - /** Compares the magnitudes of two BitArrays, ignoring their signs. - - Return values are: - - 0 if the numbers are the same - - < 0 if this number is smaller than the other - - > 0 if this number is bigger than the other - */ - int compareAbsolute (const BitArray& other) const throw(); - - /** Returns true if the value is less than zero. - - @see setNegative, negate - */ - bool isNegative() const throw(); - - /** Changes the sign of the number to be positive or negative. - - @see isNegative, negate - */ - void setNegative (const bool shouldBeNegative) throw(); - - /** Inverts the sign of the number. - - @see isNegative, setNegative - */ - void negate() throw(); - - /** Counts the total number of set bits in the array. */ - int countNumberOfSetBits() const throw(); - - /** Looks for the index of the next set bit after a given starting point. - - searches from startIndex (inclusive) upwards for the first set bit, - and returns its index. - - If no set bits are found, it returns -1. - */ - int findNextSetBit (int startIndex = 0) const throw(); - - /** Looks for the index of the next clear bit after a given starting point. - - searches from startIndex (inclusive) upwards for the first clear bit, - and returns its index. - */ - int findNextClearBit (int startIndex = 0) const throw(); - - /** Returns the index of the highest set bit in the array. - - If the array is empty, this will return -1. - */ - int getHighestBit() const throw(); - - /** Converts the array to a number string. - - Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). - - If minuimumNumCharacters is greater than 0, the returned string will be - padded with leading zeros to reach at least that length. - */ - const String toString (const int base, const int minimumNumCharacters = 1) const throw(); - - /** Converts a number string to an array. - - Any non-valid characters will be ignored. - - Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). - */ - void parseString (const String& text, - const int base) throw(); - - /** Turns the array into a block of binary data. +#ifndef __JUCE_MEMORY_JUCEHEADER__ - The data is arranged as little-endian, so the first byte of data is the low 8 bits - of the array, and so on. +#endif +#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ - @see loadFromMemoryBlock - */ - const MemoryBlock toMemoryBlock() const throw(); +/********* Start of inlined file: juce_PerformanceCounter.h *********/ +#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ +#define __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ - /** Copies a block of raw data onto this array. +/** A timer for measuring performance of code and dumping the results to a file. - The data is arranged as little-endian, so the first byte of data is the low 8 bits - of the array, and so on. + e.g. @code - @see toMemoryBlock - */ - void loadFromMemoryBlock (const MemoryBlock& data) throw(); + PerformanceCounter pc ("fish", 50, "/temp/myfishlog.txt"); - juce_UseDebuggingNewOperator + for (;;) + { + pc.start(); -private: - void ensureSize (const int numVals) throw(); - unsigned int* values; - int numValues, highestBit; - bool negative; -}; + doSomethingFishy(); -#endif // __JUCE_BITARRAY_JUCEHEADER__ -/********* End of inlined file: juce_BitArray.h *********/ + pc.stop(); + } + @endcode -/** - A simple pseudo-random number generator. + 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 Random +class JUCE_API PerformanceCounter { public: - /** Creates a Random object based on a seed value. - - For a given seed value, the subsequent numbers generated by this object - will be predictable, so a good idea is to set this value based - on the time, e.g. + /** Creates a PerformanceCounter object. - new Random (Time::currentTimeMillis()) + @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 */ - Random (const int64 seedValue) throw(); + PerformanceCounter (const String& counterName, + int runsPerPrintout = 100, + const File& loggingFile = File::nonexistent); /** Destructor. */ - ~Random() throw(); - - /** Returns the next random 32 bit integer. - - @returns a random integer from the full range 0x80000000 to 0x7fffffff - */ - int nextInt() throw(); - - /** Returns the next random number, limited to a given range. - - @returns a random integer between 0 (inclusive) and maxValue (exclusive). - */ - int nextInt (const int maxValue) throw(); - - /** Returns the next 64-bit random number. - - @returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff - */ - int64 nextInt64() throw(); - - /** Returns the next random floating-point number. - - @returns a random value in the range 0 to 1.0 - */ - float nextFloat() throw(); - - /** Returns the next random floating-point number. - - @returns a random value in the range 0 to 1.0 - */ - double nextDouble() throw(); - - /** Returns the next random boolean value. - */ - bool nextBool() throw(); + ~PerformanceCounter(); - /** Returns a BitArray containing a random number. + /** Starts timing. - @returns a random value in the range 0 to (maximumValue - 1). + @see stop */ - const BitArray nextLargeNumber (const BitArray& maximumValue) throw(); + void start(); - /** Sets a range of bits in a BitArray to random values. */ - void fillBitsRandomly (BitArray& arrayToChange, int startBit, int numBits) throw(); + /** Stops timing and prints out the results. - /** To avoid the overhead of having to create a new Random object whenever - you need a number, this is a shared application-wide object that - can be used. + The number of iterations before doing a printout of the + results is set in the constructor. - It's not thread-safe though, so threads should use their own Random object. + @see start */ - static Random& getSystemRandom() throw(); - - /** Resets this Random object to a given seed value. */ - void setSeed (const int64 newSeed) throw(); + void stop(); - /** Reseeds this generator using a value generated from various semi-random system - properties like the current time, etc. + /** Dumps the current metrics to the debugger output and to a file. - Because this function convolves the time with the last seed value, calling - it repeatedly will increase the randomness of the final result. + 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 setSeedRandomly(); + void printStatistics(); juce_UseDebuggingNewOperator private: - int64 seed; + + String name; + int numRuns, runsPerPrint; + double totalTime; + int64 started; + File outputFile; }; -#endif // __JUCE_RANDOM_JUCEHEADER__ -/********* End of inlined file: juce_Random.h *********/ +#endif // __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ +/********* End of inlined file: juce_PerformanceCounter.h *********/ #endif -#ifndef __JUCE_RELATIVETIME_JUCEHEADER__ +#ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ #endif #ifndef __JUCE_SINGLETON_JUCEHEADER__ @@ -8269,7 +7858,412 @@ private: /********* End of inlined file: juce_Singleton.h *********/ #endif -#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ +#ifndef __JUCE_RANDOM_JUCEHEADER__ + +/********* Start of inlined file: juce_Random.h *********/ +#ifndef __JUCE_RANDOM_JUCEHEADER__ +#define __JUCE_RANDOM_JUCEHEADER__ + +/********* Start of inlined file: juce_BitArray.h *********/ +#ifndef __JUCE_BITARRAY_JUCEHEADER__ +#define __JUCE_BITARRAY_JUCEHEADER__ + +class MemoryBlock; + +/** + An array of on/off bits, also usable to store large binary integers. + + A BitArray acts like an arbitrarily large integer whose bits can be set or + cleared, and some basic mathematical operations can be done on the number as + a whole. +*/ +class JUCE_API BitArray +{ +public: + + /** Creates an empty BitArray */ + BitArray() throw(); + + /** Creates a BitArray containing an integer value in its low bits. + + The low 32 bits of the array are initialised with this value. + */ + BitArray (const unsigned int value) throw(); + + /** Creates a BitArray containing an integer value in its low bits. + + The low 32 bits of the array are initialised with the absolute value + passed in, and its sign is set to reflect the sign of the number. + */ + BitArray (const int value) throw(); + + /** Creates a BitArray containing an integer value in its low bits. + + The low 64 bits of the array are initialised with the absolute value + passed in, and its sign is set to reflect the sign of the number. + */ + BitArray (int64 value) throw(); + + /** Creates a copy of another BitArray. */ + BitArray (const BitArray& other) throw(); + + /** Destructor. */ + ~BitArray() throw(); + + /** Copies another BitArray onto this one. */ + const BitArray& operator= (const BitArray& other) throw(); + + /** Two arrays are the same if the same bits are set. */ + bool operator== (const BitArray& other) const throw(); + /** Two arrays are the same if the same bits are set. */ + bool operator!= (const BitArray& other) const throw(); + + /** Clears all bits in the BitArray to 0. */ + void clear() throw(); + + /** Clears a particular bit in the array. */ + void clearBit (const int bitNumber) throw(); + + /** Sets a specified bit to 1. + + If the bit number is high, this will grow the array to accomodate it. + */ + void setBit (const int bitNumber) throw(); + + /** Sets or clears a specified bit. */ + void setBit (const int bitNumber, + const bool shouldBeSet) throw(); + + /** Sets a range of bits to be either on or off. + + @param startBit the first bit to change + @param numBits the number of bits to change + @param shouldBeSet whether to turn these bits on or off + */ + void setRange (int startBit, + int numBits, + const bool shouldBeSet) throw(); + + /** Inserts a bit an a given position, shifting up any bits above it. */ + void insertBit (const int bitNumber, + const bool shouldBeSet) throw(); + + /** Returns the value of a specified bit in the array. + + If the index is out-of-range, the result will be false. + */ + bool operator[] (const int bit) const throw(); + + /** Returns true if no bits are set. */ + bool isEmpty() const throw(); + + /** Returns a range of bits in the array as a new BitArray. + + e.g. getBitRangeAsInt (0, 64) would return the lowest 64 bits. + @see getBitRangeAsInt + */ + const BitArray getBitRange (int startBit, int numBits) const throw(); + + /** Returns a range of bits in the array as an integer value. + + e.g. getBitRangeAsInt (0, 32) would return the lowest 32 bits. + + Asking for more than 32 bits isn't allowed (obviously) - for that, use + getBitRange(). + */ + int getBitRangeAsInt (int startBit, int numBits) const throw(); + + /** Sets a range of bits in the array based on an integer value. + + Copies the given integer into the array, starting at startBit, + and only using up to numBits of the available bits. + */ + void setBitRangeAsInt (int startBit, int numBits, + unsigned int valueToSet) throw(); + + /** Performs a bitwise OR with another BitArray. + + The result ends up in this array. + */ + void orWith (const BitArray& other) throw(); + + /** Performs a bitwise AND with another BitArray. + + The result ends up in this array. + */ + void andWith (const BitArray& other) throw(); + + /** Performs a bitwise XOR with another BitArray. + + The result ends up in this array. + */ + void xorWith (const BitArray& other) throw(); + + /** Adds another BitArray's value to this one. + + Treating the two arrays as large positive integers, this + adds them up and puts the result in this array. + */ + void add (const BitArray& other) throw(); + + /** Subtracts another BitArray's value from this one. + + Treating the two arrays as large positive integers, this + subtracts them and puts the result in this array. + + Note that if the result should be negative, this won't be + handled correctly. + */ + void subtract (const BitArray& other) throw(); + + /** Multiplies another BitArray's value with this one. + + Treating the two arrays as large positive integers, this + multiplies them and puts the result in this array. + */ + void multiplyBy (const BitArray& other) throw(); + + /** Divides another BitArray's value into this one and also produces a remainder. + + Treating the two arrays as large positive integers, this + divides this value by the other, leaving the quotient in this + array, and the remainder is copied into the other BitArray passed in. + */ + void divideBy (const BitArray& divisor, BitArray& remainder) throw(); + + /** Returns the largest value that will divide both this value and the one + passed-in. + */ + const BitArray findGreatestCommonDivisor (BitArray other) const throw(); + + /** Performs a modulo operation on this value. + + The result is stored in this value. + */ + void modulo (const BitArray& divisor) throw(); + + /** Performs a combined exponent and modulo operation. + + This BitArray's value becomes (this ^ exponent) % modulus. + */ + void exponentModulo (const BitArray& exponent, const BitArray& modulus) throw(); + + /** Performs an inverse modulo on the value. + + i.e. the result is (this ^ -1) mod (modulus). + */ + void inverseModulo (const BitArray& modulus) throw(); + + /** Shifts a section of bits left or right. + + @param howManyBitsLeft how far to move the bits (+ve numbers shift it left, -ve numbers shift it right). + @param startBit the first bit to affect - if this is > 0, only bits above that index will be affected. + */ + void shiftBits (int howManyBitsLeft, + int startBit = 0) throw(); + + /** Does a signed comparison of two BitArrays. + + Return values are: + - 0 if the numbers are the same + - < 0 if this number is smaller than the other + - > 0 if this number is bigger than the other + */ + int compare (const BitArray& other) const throw(); + + /** Compares the magnitudes of two BitArrays, ignoring their signs. + + Return values are: + - 0 if the numbers are the same + - < 0 if this number is smaller than the other + - > 0 if this number is bigger than the other + */ + int compareAbsolute (const BitArray& other) const throw(); + + /** Returns true if the value is less than zero. + + @see setNegative, negate + */ + bool isNegative() const throw(); + + /** Changes the sign of the number to be positive or negative. + + @see isNegative, negate + */ + void setNegative (const bool shouldBeNegative) throw(); + + /** Inverts the sign of the number. + + @see isNegative, setNegative + */ + void negate() throw(); + + /** Counts the total number of set bits in the array. */ + int countNumberOfSetBits() const throw(); + + /** Looks for the index of the next set bit after a given starting point. + + searches from startIndex (inclusive) upwards for the first set bit, + and returns its index. + + If no set bits are found, it returns -1. + */ + int findNextSetBit (int startIndex = 0) const throw(); + + /** Looks for the index of the next clear bit after a given starting point. + + searches from startIndex (inclusive) upwards for the first clear bit, + and returns its index. + */ + int findNextClearBit (int startIndex = 0) const throw(); + + /** Returns the index of the highest set bit in the array. + + If the array is empty, this will return -1. + */ + int getHighestBit() const throw(); + + /** Converts the array to a number string. + + Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). + + If minuimumNumCharacters is greater than 0, the returned string will be + padded with leading zeros to reach at least that length. + */ + const String toString (const int base, const int minimumNumCharacters = 1) const throw(); + + /** Converts a number string to an array. + + Any non-valid characters will be ignored. + + Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). + */ + void parseString (const String& text, + const int base) throw(); + + /** Turns the array into a block of binary data. + + The data is arranged as little-endian, so the first byte of data is the low 8 bits + of the array, and so on. + + @see loadFromMemoryBlock + */ + const MemoryBlock toMemoryBlock() const throw(); + + /** Copies a block of raw data onto this array. + + The data is arranged as little-endian, so the first byte of data is the low 8 bits + of the array, and so on. + + @see toMemoryBlock + */ + void loadFromMemoryBlock (const MemoryBlock& data) throw(); + + juce_UseDebuggingNewOperator + +private: + void ensureSize (const int numVals) throw(); + unsigned int* values; + int numValues, highestBit; + bool negative; +}; + +#endif // __JUCE_BITARRAY_JUCEHEADER__ +/********* End of inlined file: juce_BitArray.h *********/ + +/** + A simple pseudo-random number generator. +*/ +class JUCE_API Random +{ +public: + + /** Creates a Random object based on a seed value. + + For a given seed value, the subsequent numbers generated by this object + will be predictable, so a good idea is to set this value based + on the time, e.g. + + new Random (Time::currentTimeMillis()) + */ + Random (const int64 seedValue) throw(); + + /** Destructor. */ + ~Random() throw(); + + /** Returns the next random 32 bit integer. + + @returns a random integer from the full range 0x80000000 to 0x7fffffff + */ + int nextInt() throw(); + + /** Returns the next random number, limited to a given range. + + @returns a random integer between 0 (inclusive) and maxValue (exclusive). + */ + int nextInt (const int maxValue) throw(); + + /** Returns the next 64-bit random number. + + @returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff + */ + int64 nextInt64() throw(); + + /** Returns the next random floating-point number. + + @returns a random value in the range 0 to 1.0 + */ + float nextFloat() throw(); + + /** Returns the next random floating-point number. + + @returns a random value in the range 0 to 1.0 + */ + double nextDouble() throw(); + + /** Returns the next random boolean value. + */ + bool nextBool() throw(); + + /** Returns a BitArray containing a random number. + + @returns a random value in the range 0 to (maximumValue - 1). + */ + const BitArray nextLargeNumber (const BitArray& maximumValue) throw(); + + /** Sets a range of bits in a BitArray to random values. */ + void fillBitsRandomly (BitArray& arrayToChange, int startBit, int numBits) throw(); + + /** To avoid the overhead of having to create a new Random object whenever + you need a number, this is a shared application-wide object that + can be used. + + It's not thread-safe though, so threads should use their own Random object. + */ + static Random& getSystemRandom() throw(); + + /** Resets this Random object to a given seed value. */ + void setSeed (const int64 newSeed) throw(); + + /** Reseeds this generator using a value generated from various semi-random system + properties like the current time, etc. + + Because this function convolves the time with the last seed value, calling + it repeatedly will increase the randomness of the final result. + */ + void setSeedRandomly(); + + juce_UseDebuggingNewOperator + +private: + int64 seed; +}; + +#endif // __JUCE_RANDOM_JUCEHEADER__ +/********* End of inlined file: juce_Random.h *********/ + +#endif +#ifndef __JUCE_RELATIVETIME_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); @@ -37243,408 +37045,166 @@ public: /** Returns the name of this format. - e.g. "WAV file" or "AIFF file" - */ - const String& getFormatName() const; - - /** Returns all the file extensions that might apply to a file of this format. - - The first item will be the one that's preferred when creating a new file. - - So for a wav file this might just return ".wav"; for an AIFF file it might - return two items, ".aif" and ".aiff" - */ - const StringArray& getFileExtensions() const; - - /** Returns true if this the given file can be read by this format. - - Subclasses shouldn't do too much work here, just check the extension or - file type. The base class implementation just checks the file's extension - against one of the ones that was registered in the constructor. - */ - virtual bool canHandleFile (const File& fileToTest); - - /** Returns a set of sample rates that the format can read and write. */ - virtual const Array getPossibleSampleRates() = 0; - - /** Returns a set of bit depths that the format can read and write. */ - virtual const Array getPossibleBitDepths() = 0; - - /** Returns true if the format can do 2-channel audio. */ - virtual bool canDoStereo() = 0; - - /** Returns true if the format can do 1-channel audio. */ - virtual bool canDoMono() = 0; - - /** Returns true if the format uses compressed data. */ - virtual bool isCompressed(); - - /** Returns a list of different qualities that can be used when writing. - - Non-compressed formats will just return an empty array, but for something - like Ogg-Vorbis or MP3, it might return a list of bit-rates, etc. - - When calling createWriterFor(), an index from this array is passed in to - tell the format which option is required. - */ - virtual const StringArray getQualityOptions(); - - /** Tries to create an object that can read from a stream containing audio - data in this format. - - The reader object that is returned can be used to read from the stream, and - should then be deleted by the caller. - - @param sourceStream the stream to read from - the AudioFormatReader object - that is returned will delete this stream when it no longer - needs it. - @param deleteStreamIfOpeningFails if no reader can be created, this determines whether this method - should delete the stream object that was passed-in. (If a valid - reader is returned, it will always be in charge of deleting the - stream, so this parameter is ignored) - @see AudioFormatReader - */ - virtual AudioFormatReader* createReaderFor (InputStream* sourceStream, - const bool deleteStreamIfOpeningFails) = 0; - - /** Tries to create an object that can write to a stream with this audio format. - - The writer object that is returned can be used to write to the stream, and - should then be deleted by the caller. - - If the stream can't be created for some reason (e.g. the parameters passed in - here aren't suitable), this will return 0. - - @param streamToWriteTo the stream that the data will go to - this will be - deleted by the AudioFormatWriter object when it's no longer - needed. If no AudioFormatWriter can be created by this method, - the stream will NOT be deleted, so that the caller can re-use it - to try to open a different format, etc - @param sampleRateToUse the sample rate for the file, which must be one of the ones - returned by getPossibleSampleRates() - @param numberOfChannels the number of channels - this must be either 1 or 2, and - the choice will depend on the results of canDoMono() and - canDoStereo() - @param bitsPerSample the bits per sample to use - this must be one of the values - returned by getPossibleBitDepths() - @param metadataValues a set of metadata values that the writer should try to write - to the stream. Exactly what these are depends on the format, - and the subclass doesn't actually have to do anything with - them if it doesn't want to. Have a look at the specific format - implementation classes to see possible values that can be - used - @param qualityOptionIndex the index of one of compression qualities returned by the - getQualityOptions() method. If there aren't any quality options - for this format, just pass 0 in this parameter, as it'll be - ignored - @see AudioFormatWriter - */ - virtual AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, - double sampleRateToUse, - unsigned int numberOfChannels, - int bitsPerSample, - const StringPairArray& metadataValues, - int qualityOptionIndex) = 0; - -protected: - /** Creates an AudioFormat object. - - @param formatName this sets the value that will be returned by getFormatName() - @param fileExtensions a zero-terminated list of file extensions - this is what will - be returned by getFileExtension() - */ - AudioFormat (const String& formatName, - const tchar** const fileExtensions); - -private: - - String formatName; - StringArray fileExtensions; -}; - -#endif // __JUCE_AUDIOFORMAT_JUCEHEADER__ -/********* End of inlined file: juce_AudioFormat.h *********/ - -/** - Reads and Writes AIFF format audio files. - - @see AudioFormat -*/ -class JUCE_API AiffAudioFormat : public AudioFormat -{ -public: - - /** Creates an format object. */ - AiffAudioFormat(); - - /** Destructor. */ - ~AiffAudioFormat(); - - const Array getPossibleSampleRates(); - const Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); -#if JUCE_MAC - bool canHandleFile (const File& fileToTest); -#endif - - 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 // __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. + e.g. "WAV file" or "AIFF file" */ - int getPositionOfTrackStart (int trackNum) const; + const String& getFormatName() const; - /** Returns true if a given track is an audio track. + /** Returns all the file extensions that might apply to a file of this format. - @param trackNum the track number, where 0 is the first track. + The first item will be the one that's preferred when creating a new file. + + So for a wav file this might just return ".wav"; for an AIFF file it might + return two items, ".aif" and ".aiff" */ - bool isTrackAudio (int trackNum) const; + const StringArray& getFileExtensions() const; - /** Refreshes the object's table of contents. + /** Returns true if this the given file can be read by this format. - 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. + Subclasses shouldn't do too much work here, just check the extension or + file type. The base class implementation just checks the file's extension + against one of the ones that was registered in the constructor. */ - void refreshTrackLengths(); + virtual bool canHandleFile (const File& fileToTest); - /** Enables scanning for indexes within tracks. + /** Returns a set of sample rates that the format can read and write. */ + virtual const Array getPossibleSampleRates() = 0; - @see getLastIndex - */ - void enableIndexScanning (bool enabled); + /** Returns a set of bit depths that the format can read and write. */ + virtual const Array getPossibleBitDepths() = 0; - /** Returns the index number found during the last read() call. + /** Returns true if the format can do 2-channel audio. */ + virtual bool canDoStereo() = 0; - Index scanning is turned off by default - turn it on with enableIndexScanning(). + /** Returns true if the format can do 1-channel audio. */ + virtual bool canDoMono() = 0; - 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. + /** Returns true if the format uses compressed data. */ + virtual bool isCompressed(); - Some devices might not support indexes, of course. + /** Returns a list of different qualities that can be used when writing. - (If you don't know what CD indexes are, it's unlikely you'll ever need them). + Non-compressed formats will just return an empty array, but for something + like Ogg-Vorbis or MP3, it might return a list of bit-rates, etc. - @see enableIndexScanning + When calling createWriterFor(), an index from this array is passed in to + tell the format which option is required. */ - int getLastIndex() const; + virtual const StringArray getQualityOptions(); - /** Scans a track to find the position of any indexes within it. + /** Tries to create an object that can read from a stream containing audio + data in this format. - @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) + The reader object that is returned can be used to read from the stream, and + should then be deleted by the caller. + + @param sourceStream the stream to read from - the AudioFormatReader object + that is returned will delete this stream when it no longer + needs it. + @param deleteStreamIfOpeningFails if no reader can be created, this determines whether this method + should delete the stream object that was passed-in. (If a valid + reader is returned, it will always be in charge of deleting the + stream, so this parameter is ignored) + @see AudioFormatReader */ - const Array findIndexesInTrack (const int trackNumber); + virtual AudioFormatReader* createReaderFor (InputStream* sourceStream, + const bool deleteStreamIfOpeningFails) = 0; - /** Returns the CDDB id number for the CD. + /** Tries to create an object that can write to a stream with this audio format. - It's not a great way of identifying a disc, but it's traditional. - */ - int getCDDBId(); + The writer object that is returned can be used to write to the stream, and + should then be deleted by the caller. - /** Tries to eject the disk. + If the stream can't be created for some reason (e.g. the parameters passed in + here aren't suitable), this will return 0. - Of course this might not be possible, if some other process is using it. + @param streamToWriteTo the stream that the data will go to - this will be + deleted by the AudioFormatWriter object when it's no longer + needed. If no AudioFormatWriter can be created by this method, + the stream will NOT be deleted, so that the caller can re-use it + to try to open a different format, etc + @param sampleRateToUse the sample rate for the file, which must be one of the ones + returned by getPossibleSampleRates() + @param numberOfChannels the number of channels - this must be either 1 or 2, and + the choice will depend on the results of canDoMono() and + canDoStereo() + @param bitsPerSample the bits per sample to use - this must be one of the values + returned by getPossibleBitDepths() + @param metadataValues a set of metadata values that the writer should try to write + to the stream. Exactly what these are depends on the format, + and the subclass doesn't actually have to do anything with + them if it doesn't want to. Have a look at the specific format + implementation classes to see possible values that can be + used + @param qualityOptionIndex the index of one of compression qualities returned by the + getQualityOptions() method. If there aren't any quality options + for this format, just pass 0 in this parameter, as it'll be + ignored + @see AudioFormatWriter */ - void ejectDisk(); + virtual AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex) = 0; - juce_UseDebuggingNewOperator +protected: + /** Creates an AudioFormat object. + + @param formatName this sets the value that will be returned by getFormatName() + @param fileExtensions a zero-terminated list of file extensions - this is what will + be returned by getFileExtension() + */ + AudioFormat (const String& formatName, + const tchar** const fileExtensions); private: -#if JUCE_MAC - File volumeDir; - OwnedArray tracks; - Array trackStartSamples; - int currentReaderTrack; - AudioFormatReader* reader; - AudioCDReader (const File& volume); + String formatName; + StringArray fileExtensions; +}; + +#endif // __JUCE_AUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_AudioFormat.h *********/ + +/** + Reads and Writes AIFF format audio files. + + @see AudioFormat +*/ +class JUCE_API AiffAudioFormat : public AudioFormat +{ 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); + /** Creates an format object. */ + AiffAudioFormat(); -#elif JUCE_LINUX - AudioCDReader(); + /** Destructor. */ + ~AiffAudioFormat(); + + const Array getPossibleSampleRates(); + const Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); +#if JUCE_MAC + bool canHandleFile (const File& fileToTest); #endif - AudioCDReader (const AudioCDReader&); - const AudioCDReader& operator= (const AudioCDReader&); + 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_AUDIOCDREADER_JUCEHEADER__ -/********* End of inlined file: juce_AudioCDReader.h *********/ +#endif // __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ +/********* End of inlined file: juce_AiffAudioFormat.h *********/ #endif #ifndef __JUCE_AUDIOFORMAT_JUCEHEADER__ @@ -38107,6 +37667,287 @@ public: #endif // __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ /********* End of inlined file: juce_FlacAudioFormat.h *********/ +#endif +#ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ + +/********* Start of inlined file: juce_WavAudioFormat.h *********/ +#ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ +#define __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ + +/** + Reads and Writes WAV format audio files. + + @see AudioFormat +*/ +class JUCE_API WavAudioFormat : public AudioFormat +{ +public: + + /** Creates a format object. */ + WavAudioFormat(); + + /** Destructor. */ + ~WavAudioFormat(); + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavDescription; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavOriginator; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavOriginatorRef; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + Date format is: yyyy-mm-dd + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavOriginationDate; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + Time format is: hh-mm-ss + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavOriginationTime; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + This is the number of samples from the start of an edit that the + file is supposed to begin at. Seems like an obvious mistake to + only allow a file to occur in an edit once, but that's the way + it is.. + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavTimeReference; + + /** Metadata property name used by wav readers and writers for adding + a BWAV chunk to the file. + + This is a + + @see AudioFormatReader::metadataValues, createWriterFor + */ + static const tchar* const bwavCodingHistory; + + /** Utility function to fill out the appropriate metadata for a BWAV file. + + This just makes it easier than using the property names directly, and it + fills out the time and date in the right format. + */ + static const StringPairArray createBWAVMetadata (const String& description, + const String& originator, + const String& originatorRef, + const Time& dateAndTime, + const int64 timeReferenceSamples, + const String& codingHistory); + + 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); + + /** Utility function to replace the metadata in a wav file with a new set of values. + + If possible, this cheats by overwriting just the metadata region of the file, rather + than by copying the whole file again. + */ + bool replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata); + + juce_UseDebuggingNewOperator +}; + +#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__ @@ -38216,126 +38057,87 @@ public: /********* End of inlined file: juce_QuickTimeAudioFormat.h *********/ #endif -#ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ -/********* Start of inlined file: juce_WavAudioFormat.h *********/ -#ifndef __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ -#define __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ +/********* Start of inlined file: juce_AudioCDBurner.h *********/ +#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ +#define __JUCE_AUDIOCDBURNER_JUCEHEADER__ -/** - Reads and Writes WAV format audio files. +#if JUCE_USE_CDBURNER - @see AudioFormat +/** */ -class JUCE_API WavAudioFormat : public AudioFormat +class AudioCDBurner { -public: - - /** Creates a format object. */ - WavAudioFormat(); - - /** Destructor. */ - ~WavAudioFormat(); - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. - - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const tchar* const bwavDescription; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. - - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const tchar* const bwavOriginator; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. - - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const tchar* const bwavOriginatorRef; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. +public: - Date format is: yyyy-mm-dd + /** Returns a list of available optical drives. - @see AudioFormatReader::metadataValues, createWriterFor + Use openDevice() to open one of the items from this list. */ - static const tchar* const bwavOriginationDate; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. + static const StringArray findAvailableDevices(); - Time format is: hh-mm-ss + /** Tries to open one of the optical drives. - @see AudioFormatReader::metadataValues, createWriterFor + The deviceIndex is an index into the array returned by findAvailableDevices(). */ - static const tchar* const bwavOriginationTime; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. + static AudioCDBurner* openDevice (const int deviceIndex); - This is the number of samples from the start of an edit that the - file is supposed to begin at. Seems like an obvious mistake to - only allow a file to occur in an edit once, but that's the way - it is.. + /** Destructor. */ + ~AudioCDBurner(); - @see AudioFormatReader::metadataValues, createWriterFor + /** Returns true if there's a writable disk in the drive. */ - static const tchar* const bwavTimeReference; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. + bool isDiskPresent() const; - This is a + /** Returns the number of free blocks on the disk. - @see AudioFormatReader::metadataValues, createWriterFor + There are 75 blocks per second, at 44100Hz. */ - static const tchar* const bwavCodingHistory; + int getNumAvailableAudioBlocks() const; - /** Utility function to fill out the appropriate metadata for a BWAV file. + /** Adds a track to be written. - This just makes it easier than using the property names directly, and it - fills out the time and date in the right format. + 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. */ - static const StringPairArray createBWAVMetadata (const String& description, - const String& originator, - const String& originatorRef, - const Time& dateAndTime, - const int64 timeReferenceSamples, - const String& codingHistory); + bool addAudioTrack (AudioSource* source, int numSamples); - const Array getPossibleSampleRates(); - const Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); + /** - AudioFormatReader* createReaderFor (InputStream* sourceStream, - const bool deleteStreamIfOpeningFails); + Return true to cancel the current burn operation + */ + class BurnProgressListener + { + public: + BurnProgressListener() throw() {} + virtual ~BurnProgressListener() {} - AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, - double sampleRateToUse, - unsigned int numberOfChannels, - int bitsPerSample, - const StringPairArray& metadataValues, - int qualityOptionIndex); + /** Called at intervals to report on the progress of the AudioCDBurner. - /** Utility function to replace the metadata in a wav file with a new set of values. + To cancel the burn, return true from this. + */ + virtual bool audioCDBurnProgress (float proportionComplete) = 0; + }; - If possible, this cheats by overwriting just the metadata region of the file, rather - than by copying the whole file again. - */ - bool replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata); + const String burn (BurnProgressListener* listener, + const bool ejectDiscAfterwards, + const bool peformFakeBurnForTesting); juce_UseDebuggingNewOperator + +private: + AudioCDBurner (const int deviceIndex); + + void* internal; }; -#endif // __JUCE_WAVAUDIOFORMAT_JUCEHEADER__ -/********* End of inlined file: juce_WavAudioFormat.h *********/ +#endif +#endif // __JUCE_AUDIOCDBURNER_JUCEHEADER__ +/********* End of inlined file: juce_AudioCDBurner.h *********/ #endif #ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__ @@ -38994,612 +38796,183 @@ class JUCE_API MessageManagerLock { public: - /** Tries to acquire a lock on the message manager. - - The constructor attempts to gain a lock on the message loop, and the lock will be - kept for the lifetime of this object. - - Optionally, you can pass a thread object here, and while waiting to obtain the lock, - this method will keep checking whether the thread has been given the - Thread::signalThreadShouldExit() signal. If this happens, then it will return - without gaining the lock. If you pass a thread, you must check whether the lock was - successful by calling lockWasGained(). If this is false, your thread is being told to - die, so you should take evasive action. - - If you pass zero for the thread object, it will wait indefinitely for the lock - be - careful when doing this, because it's very easy to deadlock if your message thread - attempts to call stopThread() on a thread just as that thread attempts to get the - message lock. - - If the calling thread already has the lock, nothing will be done, so it's safe and - quick to use these locks recursively. - - E.g. - @code - void run() - { - ... - - while (! threadShouldExit()) - { - MessageManagerLock mml (Thread::getCurrentThread()); - - if (! mml.lockWasGained()) - return; // another thread is trying to kill us! - - ..do some locked stuff here.. - } - - ..and now the MM is now unlocked.. - } - @endcode - - */ - MessageManagerLock (Thread* const threadToCheckForExitSignal = 0) throw(); - - /** This has the same behaviour as the other constructor, but takes a ThreadPoolJob - instead of a thread. - - See the MessageManagerLock (Thread*) constructor for details on how this works. - */ - MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) throw(); - - /** Releases the current thread's lock on the message manager. - - Make sure this object is created and deleted by the same thread, - otherwise there are no guarantees what will happen! - */ - ~MessageManagerLock() throw(); - - /** Returns true if the lock was successfully acquired. - - (See the constructor that takes a Thread for more info). - */ - bool lockWasGained() const throw() { return locked; } - -private: - bool locked, needsUnlocking; - void* sharedEvents; - - void init (Thread* const thread, ThreadPoolJob* const job) throw(); -}; - -#endif // __JUCE_MESSAGEMANAGER_JUCEHEADER__ -/********* End of inlined file: juce_MessageManager.h *********/ - -#endif -#ifndef __JUCE_MULTITIMER_JUCEHEADER__ - -/********* Start of inlined file: juce_MultiTimer.h *********/ -#ifndef __JUCE_MULTITIMER_JUCEHEADER__ -#define __JUCE_MULTITIMER_JUCEHEADER__ - -/** - A type of timer class that can run multiple timers with different frequencies, - all of which share a single callback. - - This class is very similar to the Timer class, but allows you run multiple - separate timers, where each one has a unique ID number. The methods in this - class are exactly equivalent to those in Timer, but with the addition of - this ID number. - - To use it, you need to create a subclass of MultiTimer, implementing the - timerCallback() method. Then you can start timers with startTimer(), and - each time the callback is triggered, it passes in the ID of the timer that - caused it. - - @see Timer -*/ -class JUCE_API MultiTimer -{ -protected: - - /** Creates a MultiTimer. - - When created, no timers are running, so use startTimer() to start things off. - */ - MultiTimer() throw(); - - /** Creates a copy of another timer. - - Note that this timer will not contain any running timers, even if the one you're - copying from was running. - */ - MultiTimer (const MultiTimer& other) throw(); - -public: - - /** Destructor. */ - virtual ~MultiTimer(); - - /** The user-defined callback routine that actually gets called by each of the - timers that are running. - - It's perfectly ok to call startTimer() or stopTimer() from within this - callback to change the subsequent intervals. - */ - virtual void timerCallback (const int timerId) = 0; - - /** Starts a timer and sets the length of interval required. - - If the timer is already started, this will reset it, so the - time between calling this method and the next timer callback - will not be less than the interval length passed in. - - @param timerId a unique Id number that identifies the timer to - start. This is the id that will be passed back - to the timerCallback() method when this timer is - triggered - @param intervalInMilliseconds the interval to use (any values less than 1 will be - rounded up to 1) - */ - void startTimer (const int timerId, const int intervalInMilliseconds) throw(); - - /** Stops a timer. - - If a timer has been started with the given ID number, it will be cancelled. - No more callbacks will be made for the specified timer after this method returns. - - If this is called from a different thread, any callbacks that may - be currently executing may be allowed to finish before the method - returns. - */ - void stopTimer (const int timerId) throw(); - - /** Checks whether a timer has been started for a specified ID. - - @returns true if a timer with the given ID is running. - */ - bool isTimerRunning (const int timerId) const throw(); - - /** Returns the interval for a specified timer ID. - - @returns the timer's interval in milliseconds if it's running, or 0 if it's no timer - is running for the ID number specified. - */ - int getTimerInterval (const int timerId) const throw(); - -private: - CriticalSection timerListLock; - VoidArray timers; - - const MultiTimer& operator= (const MultiTimer&); -}; - -#endif // __JUCE_MULTITIMER_JUCEHEADER__ -/********* End of inlined file: juce_MultiTimer.h *********/ - -#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; } + /** Tries to acquire a lock on the message manager. - /** 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; } + The constructor attempts to gain a lock on the message loop, and the lock will be + kept for the lifetime of this object. - uint8* data; - int lineStride, pixelStride, width, height; - }; + Optionally, you can pass a thread object here, and while waiting to obtain the lock, + this method will keep checking whether the thread has been given the + Thread::signalThreadShouldExit() signal. If this happens, then it will return + without gaining the lock. If you pass a thread, you must check whether the lock was + successful by calling lockWasGained(). If this is false, your thread is being told to + die, so you should take evasive action. - /** Copies some pixel values to a rectangle of the image. + If you pass zero for the thread object, it will wait indefinitely for the lock - be + careful when doing this, because it's very easy to deadlock if your message thread + attempts to call stopThread() on a thread just as that thread attempts to get the + message lock. - 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); + If the calling thread already has the lock, nothing will be done, so it's safe and + quick to use these locks recursively. - /** 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); + E.g. + @code + void run() + { + ... - /** Creates a RectangleList containing rectangles for all non-transparent pixels - of the image. + while (! threadShouldExit()) + { + MessageManagerLock mml (Thread::getCurrentThread()); - @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; + if (! mml.lockWasGained()) + return; // another thread is trying to kill us! - juce_UseDebuggingNewOperator + ..do some locked stuff here.. + } - /** Creates a context suitable for drawing onto this image. + ..and now the MM is now unlocked.. + } + @endcode - Don't call this method directly! It's used internally by the Graphics class. */ - virtual LowLevelGraphicsContext* createLowLevelContext(); + MessageManagerLock (Thread* const threadToCheckForExitSignal = 0) throw(); -protected: - friend class BitmapData; - const PixelFormat format; - const int imageWidth, imageHeight; + /** This has the same behaviour as the other constructor, but takes a ThreadPoolJob + instead of a thread. - /** Used internally so that subclasses can call a constructor that doesn't allocate memory */ - Image (const PixelFormat format, - const int imageWidth, - const int imageHeight); + See the MessageManagerLock (Thread*) constructor for details on how this works. + */ + MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal) throw(); - int pixelStride, lineStride; - uint8* imageData; + /** Releases the current thread's lock on the message manager. + + Make sure this object is created and deleted by the same thread, + otherwise there are no guarantees what will happen! + */ + ~MessageManagerLock() throw(); + + /** Returns true if the lock was successfully acquired. + + (See the constructor that takes a Thread for more info). + */ + bool lockWasGained() const throw() { return locked; } private: + bool locked, needsUnlocking; + void* sharedEvents; - const Image& operator= (const Image&); + void init (Thread* const thread, ThreadPoolJob* const job) throw(); }; -#endif // __JUCE_IMAGE_JUCEHEADER__ -/********* End of inlined file: juce_Image.h *********/ +#endif // __JUCE_MESSAGEMANAGER_JUCEHEADER__ +/********* End of inlined file: juce_MessageManager.h *********/ + +#endif +#ifndef __JUCE_MULTITIMER_JUCEHEADER__ + +/********* Start of inlined file: juce_MultiTimer.h *********/ +#ifndef __JUCE_MULTITIMER_JUCEHEADER__ +#define __JUCE_MULTITIMER_JUCEHEADER__ /** - A Brush that fills areas with tiled repetitions of an image. + A type of timer class that can run multiple timers with different frequencies, + all of which share a single callback. + + This class is very similar to the Timer class, but allows you run multiple + separate timers, where each one has a unique ID number. The methods in this + class are exactly equivalent to those in Timer, but with the addition of + this ID number. + + To use it, you need to create a subclass of MultiTimer, implementing the + timerCallback() method. Then you can start timers with startTimer(), and + each time the callback is triggered, it passes in the ID of the timer that + caused it. - @see Brush, Graphics::setBrush, SolidColourBrush, GradientBrush + @see Timer */ -class JUCE_API ImageBrush : public Brush +class JUCE_API MultiTimer { -public: +protected: + + /** Creates a MultiTimer. + + When created, no timers are running, so use startTimer() to start things off. + */ + MultiTimer() throw(); - /* Creates an image brush, ready for use in Graphics::setBrush(). + /** Creates a copy of another timer. - (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 + Note that this timer will not contain any running timers, even if the one you're + copying from was running. */ - ImageBrush (Image* const image, - const int anchorX, - const int anchorY, - const float opacity) throw(); + MultiTimer (const MultiTimer& other) throw(); - /** Destructor. */ - ~ImageBrush() throw(); +public: - /** Returns the image currently being used. */ - Image* getImage() const throw() { return image; } + /** Destructor. */ + virtual ~MultiTimer(); - /** Returns the current anchor X position. */ - int getAnchorX() const throw() { return anchorX; } + /** The user-defined callback routine that actually gets called by each of the + timers that are running. - /** Returns the current anchor Y position. */ - int getAnchorY() const throw() { return anchorY; } + It's perfectly ok to call startTimer() or stopTimer() from within this + callback to change the subsequent intervals. + */ + virtual void timerCallback (const int timerId) = 0; - /** Returns the current opacity. */ - float getOpacity() const throw() { return opacity; } + /** Starts a timer and sets the length of interval required. - Brush* createCopy() const throw(); + If the timer is already started, this will reset it, so the + time between calling this method and the next timer callback + will not be less than the interval length passed in. - void applyTransform (const AffineTransform& transform) throw(); + @param timerId a unique Id number that identifies the timer to + start. This is the id that will be passed back + to the timerCallback() method when this timer is + triggered + @param intervalInMilliseconds the interval to use (any values less than 1 will be + rounded up to 1) + */ + void startTimer (const int timerId, const int intervalInMilliseconds) throw(); - void multiplyOpacity (const float multiple) throw(); + /** Stops a timer. - bool isInvisible() const throw(); + If a timer has been started with the given ID number, it will be cancelled. + No more callbacks will be made for the specified timer after this method returns. - void paintPath (LowLevelGraphicsContext& context, - const Path& path, const AffineTransform& transform) throw(); + If this is called from a different thread, any callbacks that may + be currently executing may be allowed to finish before the method + returns. + */ + void stopTimer (const int timerId) throw(); - void paintRectangle (LowLevelGraphicsContext& context, - int x, int y, int w, int h) throw(); + /** Checks whether a timer has been started for a specified ID. - void paintAlphaChannel (LowLevelGraphicsContext& context, - const Image& alphaChannelImage, int imageX, int imageY, - int x, int y, int w, int h) throw(); + @returns true if a timer with the given ID is running. + */ + bool isTimerRunning (const int timerId) const throw(); - juce_UseDebuggingNewOperator + /** Returns the interval for a specified timer ID. -protected: - Image* image; - int anchorX, anchorY; - float opacity; + @returns the timer's interval in milliseconds if it's running, or 0 if it's no timer + is running for the ID number specified. + */ + int getTimerInterval (const int timerId) const throw(); private: - ImageBrush (const ImageBrush&); - const ImageBrush& operator= (const ImageBrush&); + CriticalSection timerListLock; + VoidArray timers; - void getStartXY (int& x, int& y) const throw(); + const MultiTimer& operator= (const MultiTimer&); }; -#endif // __JUCE_IMAGEBRUSH_JUCEHEADER__ -/********* End of inlined file: juce_ImageBrush.h *********/ - -#endif -#ifndef __JUCE_SOLIDCOLOURBRUSH_JUCEHEADER__ +#endif // __JUCE_MULTITIMER_JUCEHEADER__ +/********* End of inlined file: juce_MultiTimer.h *********/ #endif -#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ +#ifndef __JUCE_TIMER_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; - - /** 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; + 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 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 saveState(); - void restoreState(); + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform); - bool clipRegionIntersects (int x, int y, int w, int h); + bool clipRegionIntersects (const Rectangle& r); const Rectangle getClipBounds() const; bool isClipEmpty() const; + void saveState(); + void restoreState(); + 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__ @@ -41628,88 +41215,162 @@ public: juce_UseDebuggingNewOperator private: - OwnedArray drawables; - OwnedArray transforms; + OwnedArray drawables; + OwnedArray transforms; + + DrawableComposite (const DrawableComposite&); + const DrawableComposite& operator= (const DrawableComposite&); +}; + +#endif // __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ +/********* End of inlined file: juce_DrawableComposite.h *********/ + +#endif +#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ + +/********* Start of inlined file: juce_DrawableImage.h *********/ +#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ +#define __JUCE_DRAWABLEIMAGE_JUCEHEADER__ + +/** + A drawable object which is a bitmap image. + + @see Drawable +*/ +class JUCE_API DrawableImage : public Drawable +{ +public: + + DrawableImage(); + + /** Destructor. */ + virtual ~DrawableImage(); + + /** Sets the image that this drawable will render. + + An internal copy is made of the image passed-in. If you want to provide an + image that this object can take charge of without needing to create a copy, + use the other setImage() method. + */ + void setImage (const Image& imageToCopy); + + /** Sets the image that this drawable will render. + + An internal copy of this will not be made, so the caller mustn't delete + the image while it's still being used by this object. + + A good way to use this is with the ImageCache - if you create an image + with ImageCache and pass it in here with releaseWhenNotNeeded = true, then + it'll be released neatly with its reference count being decreased. + + @param imageToUse the image to render + @param releaseWhenNotNeeded if false, a simple pointer is kept to the image; if true, + then the image will be deleted when this object no longer + needs it - unless the image was created by the ImageCache, + in which case it will be released with ImageCache::release(). + */ + void setImage (Image* imageToUse, + const bool releaseWhenNotNeeded); + + /** Returns the current image. */ + Image* getImage() const throw() { return image; } + + /** Clears (and possibly deletes) the currently set image. */ + void clearImage(); + + /** Sets the opacity to use when drawing the image. */ + void setOpacity (const float newOpacity); + + /** Returns the image's opacity. */ + float getOpacity() const throw() { return opacity; } + + /** Sets a colour to draw over the image's alpha channel. + + By default this is transparent so isn't drawn, but if you set a non-transparent + colour here, then it will be overlaid on the image, using the image's alpha + channel as a mask. + + This is handy for doing things like darkening or lightening an image by overlaying + it with semi-transparent black or white. + */ + void setOverlayColour (const Colour& newOverlayColour); + + /** Returns the overlay colour. */ + const Colour& getOverlayColour() const throw() { return overlayColour; } + + /** @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: + Image* image; + bool canDeleteImage; + float opacity; + Colour overlayColour; - DrawableComposite (const DrawableComposite&); - const DrawableComposite& operator= (const DrawableComposite&); + DrawableImage (const DrawableImage&); + const DrawableImage& operator= (const DrawableImage&); }; -#endif // __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ -/********* End of inlined file: juce_DrawableComposite.h *********/ +#endif // __JUCE_DRAWABLEIMAGE_JUCEHEADER__ +/********* End of inlined file: juce_DrawableImage.h *********/ #endif -#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ +#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ -/********* Start of inlined file: juce_DrawableImage.h *********/ -#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ -#define __JUCE_DRAWABLEIMAGE_JUCEHEADER__ +/********* Start of inlined file: juce_DrawableText.h *********/ +#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ +#define __JUCE_DRAWABLETEXT_JUCEHEADER__ /** - A drawable object which is a bitmap image. + A drawable object which renders a line of text. @see Drawable */ -class JUCE_API DrawableImage : public Drawable +class JUCE_API DrawableText : public Drawable { public: - DrawableImage(); + /** Creates a DrawableText object. */ + DrawableText(); /** Destructor. */ - virtual ~DrawableImage(); - - /** Sets the image that this drawable will render. - - An internal copy is made of the image passed-in. If you want to provide an - image that this object can take charge of without needing to create a copy, - use the other setImage() method. - */ - void setImage (const Image& imageToCopy); - - /** Sets the image that this drawable will render. + virtual ~DrawableText(); - An internal copy of this will not be made, so the caller mustn't delete - the image while it's still being used by this object. + /** Sets the block of text to render */ + void setText (const GlyphArrangement& newText); - A good way to use this is with the ImageCache - if you create an image - with ImageCache and pass it in here with releaseWhenNotNeeded = true, then - it'll be released neatly with its reference count being decreased. + /** Sets a single line of text to render. - @param imageToUse the image to render - @param releaseWhenNotNeeded if false, a simple pointer is kept to the image; if true, - then the image will be deleted when this object no longer - needs it - unless the image was created by the ImageCache, - in which case it will be released with ImageCache::release(). + This is a convenient method of adding a single line - for + more complex text, use the setText() that takes a + GlyphArrangement instead. */ - void setImage (Image* imageToUse, - const bool releaseWhenNotNeeded); - - /** Returns the current image. */ - Image* getImage() const throw() { return image; } - - /** Clears (and possibly deletes) the currently set image. */ - void clearImage(); - - /** Sets the opacity to use when drawing the image. */ - void setOpacity (const float newOpacity); - - /** Returns the image's opacity. */ - float getOpacity() const throw() { return opacity; } - - /** Sets a colour to draw over the image's alpha channel. + void setText (const String& newText, const Font& fontToUse); - By default this is transparent so isn't drawn, but if you set a non-transparent - colour here, then it will be overlaid on the image, using the image's alpha - channel as a mask. + /** Returns the text arrangement that was set with setText(). */ + const GlyphArrangement& getText() const throw() { return text; } - This is handy for doing things like darkening or lightening an image by overlaying - it with semi-transparent black or white. - */ - void setOverlayColour (const Colour& newOverlayColour); + /** Sets the colour of the text. */ + void setColour (const Colour& newColour); - /** Returns the overlay colour. */ - const Colour& getOverlayColour() const throw() { return overlayColour; } + /** Returns the current text colour. */ + const Colour& getColour() const throw() { return colour; } /** @internal */ void render (const Drawable::RenderingContext& context) const; @@ -41731,17 +41392,15 @@ public: juce_UseDebuggingNewOperator private: - Image* image; - bool canDeleteImage; - float opacity; - Colour overlayColour; + GlyphArrangement text; + Colour colour; - DrawableImage (const DrawableImage&); - const DrawableImage& operator= (const DrawableImage&); + DrawableText (const DrawableText&); + const DrawableText& operator= (const DrawableText&); }; -#endif // __JUCE_DRAWABLEIMAGE_JUCEHEADER__ -/********* End of inlined file: juce_DrawableImage.h *********/ +#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; + + 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 ((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))); + 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()) - { - 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 (y < 0 || y >= bounds.getHeight()) + return; - if (clipped.getRight() < bounds.getRight()) - bounds.setRight (clipped.getRight()); + needToCheckEmptinesss = true; - for (int i = top; --i >= 0;) - table [lineStrideElements * i] = 0; - - int imageX = clipped.getX() - x; - int imageY = clipped.getY() - y; + if (numPixels <= 0) + { + table [lineStrideElements * y] = 0; + return; + } - int* temp = (int*) alloca ((clipped.getWidth() * 2 + 4) * sizeof (int)); + int* tempLine = (int*) alloca ((numPixels * 2 + 4) * sizeof (int)); + int destIndex = 0, lastLevel = 0; - Image::BitmapData srcData (image, imageX, imageY, clipped.getWidth(), clipped.getHeight()); + while (--numPixels >= 0) + { + const int alpha = *mask; + mask += maskStride; - for (int i = 0; i < clipped.getHeight(); ++i) + if (alpha != lastLevel) { - 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); + tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = alpha; + lastLevel = alpha; + } - 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; - } - } - } + ++x; + } - if (lastLevel > 0) - { - temp[++destIndex] = clipped.getRight() << 8; - temp[++destIndex] = 0; - } + if (lastLevel > 0) + { + tempLine[++destIndex] = (x << 8); + tempLine[++destIndex] = 0; + } - temp[0] = destIndex >> 1; + tempLine[0] = destIndex >> 1; - intersectWithEdgeTableLine (top + i, temp); - ++y; - } - } + intersectWithEdgeTableLine (y, tempLine); } -bool EdgeTable::isEmpty() const throw() +bool EdgeTable::isEmpty() throw() { - int* t = table; - - for (int i = bounds.getHeight(); --i >= 0;) + if (needToCheckEmptinesss) { - if (t[0] > 1) - return false; + needToCheckEmptinesss = false; + int* t = table; - t += lineStrideElements; + for (int i = bounds.getHeight(); --i >= 0;) + { + if (t[0] > 1) + return false; + + t += lineStrideElements; + } + + bounds.setHeight (0); } - 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); + context->saveState(); + context->clipToImageAlpha (*imageToDraw, srcClip, transform); + fillAll(); + context->restoreState(); } - else if (fillAlphaChannelWithCurrentBrush) + else { - Path p; - p.addRectangle ((float) sourceClipX, (float) sourceClipY, - (float) sourceClipWidth, (float) sourceClipHeight); + context->drawImage (*imageToDraw, srcClip, transform, false); + } + } +} - p.applyTransform (transform); +//============================================================================== +Graphics::FillType::FillType() throw() + : colour (0xff000000), gradient (0), + image (0), imageX (0), imageY (0) +{ +} - 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; +Graphics::FillType::FillType (const Colour& colour_) throw() + : colour (colour_), gradient (0), + image (0), imageX (0), imageY (0) +{ +} - 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(); +Graphics::FillType::FillType (const ColourGradient& gradient) throw() + : colour (0xff000000), gradient (new ColourGradient (gradient)), + image (0), imageX (0), imageY (0) +{ +} - if (context->reduceClipRegion (tx, ty, tw, th)) - context->fillAlphaChannel (temp, tx, ty); +Graphics::FillType::FillType (Image* const image_, const int x, const int y) throw() + : colour (0xff000000), gradient (0), + image (image_), imageX (x), imageY (y) +{ +} - context->restoreState(); - } - else - { - state->brush->paintAlphaChannel (*context, temp, tx, ty, tx, ty, tw, th); - } - } - } - else - { - context->blendImageWarping (*imageToDraw, - sourceClipX, - sourceClipY, - sourceClipWidth, - sourceClipHeight, - transform); - } +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 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; }; 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); - } + 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; } - 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); - } + 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 (fillType.colour.getPixelARGB()); + + switch (image.getFormat()) + { + 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; } - else if (image.getFormat() == Image::SingleChannel) + } + } + + //============================================================================== + void renderImage (Image& destImage, const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& t, const EdgeTable* const tiledFillClipRegion) throw() + { + 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) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); + + if ((! betterQuality) || ((tx | ty) & 224) == 0) { - jassert (destData.pixelStride == 4); + const Rectangle srcRect (srcClip.translated ((tx + 128) >> 8, (ty + 128) >> 8)); - if (g2.isRadial) + if (tiledFillClipRegion != 0) { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); - } + blittedRenderImage3 (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, srcRect.getX(), srcRect.getY()); } else { - GradientEdgeTableRenderer renderer (destData, g2, lookupTable, numLookupEntries); - et.iterate (renderer); + 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; } + } - juce_free (lookupTable); + if (transform.isSingularity()) + return; + + if (tiledFillClipRegion != 0) + { + transformedRenderImage3 (sourceImage, destImage, *tiledFillClipRegion, destData, srcData, alpha, transform, betterQuality); } else { - const PixelARGB fillColour (colour.getPixelARGB()); + Path p; + p.addRectangle (srcClip); - if (replaceContents) - { - 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); - } - } + EdgeTable et (edgeTable->edgeTable.getMaximumBounds(), p, transform); + et.clipToEdgeTable (edgeTable->edgeTable); + + if (! et.isEmpty()) + transformedRenderImage3 (sourceImage, destImage, et, destData, srcData, alpha, transform, betterQuality); } } - void renderImage (Image& destImage, const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& t) throw() + //============================================================================== + void clipToImageAlpha (const Image& image, const Rectangle& srcClip, const AffineTransform& transform) throw() { - if (t.isSingularity()) + 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); - const AffineTransform transform (t.translated ((float) xOffset, (float) yOffset)); + EdgeTable& et = edgeTable->edgeTable; 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 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; } } - 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()) + if (transform.isSingularity()) { - 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; + et.clipToRectangle (Rectangle()); + return; + } - default: - jassert (sourceImage.getFormat() == Image::SingleChannel); + { + Path p; + p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); + EdgeTable et2 (et.getMaximumBounds(), p, transform); + et.clipToEdgeTable (et2); + } - 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); - } + if (! et.isEmpty()) + { + if (image.getFormat() == Image::ARGB) + transformedClipImage (et, srcData, transform, betterQuality); else - { - jassert (destImage.getFormat() == Image::SingleChannel) - TransformedImageFillEdgeTableRenderer renderer (destData, srcData, transform, extraAlpha, betterQuality); - et.iterate (renderer); - } - break; + transformedClipImage (et, srcData, transform, betterQuality); } } - void renderImage (Image& destImage, const Image& sourceImage, int imageX, int imageY) throw() + template + void transformedClipImage (EdgeTable& et, const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality) 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); + TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); - const int alpha = colour.getAlpha(); + for (int y = 0; y < et.getMaximumBounds().getHeight(); ++y) + renderer.clipEdgeTableLine (et, et.getMaximumBounds().getX(), y + et.getMaximumBounds().getY(), + et.getMaximumBounds().getWidth()); + } - switch (destImage.getFormat()) - { - 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; + 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); - 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; + ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); - 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; - } + 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) -{ - 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) +void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { - 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 saveState(); - void restoreState(); + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform); - bool clipRegionIntersects (int x, int y, int w, int h); + bool clipRegionIntersects (const Rectangle& r); const Rectangle getClipBounds() const; bool isClipEmpty() const; + void saveState(); + void restoreState(); + //============================================================================== 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); 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" +#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_TYPEFACE_JUCEHEADER__ - #include "gui/graphics/fonts/juce_Typeface.h" +#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" +#ifndef __JUCE_POINT_JUCEHEADER__ + #include "gui/graphics/geometry/juce_Point.h" #endif -#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ - #include "gui/graphics/geometry/juce_PathIterator.h" +#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_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_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,40 +128,44 @@ 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 saveState() + void clipToPath (const Path& path, const AffineTransform& transform) { - CGContextSaveGState (context); - stateStack.add (new SavedState (*state)); + createPath (path, transform); + CGContextClip (context); } - void restoreState() + void clipToImageAlpha (const Image& sourceImage, const Rectangle& srcClip, const AffineTransform& transform) { - CGContextRestoreGState (context); + if (! transform.isSingularity()) + { + Image* singleChannelImage = createAlphaChannelImage (sourceImage); + CGImageRef image = createImage (*singleChannelImage, true); - SavedState* const top = stateStack.getLast(); + flip(); + AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + applyTransform (t); - if (top != 0) - { - delete state; - state = top; - stateStack.removeLast (1, false); - } - else - { - jassertfalse // trying to pop with an empty stack! + CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + CGContextClipToMask (context, r, image); + + applyTransform (t.inverted()); + flip(); + + CGImageRelease (image); + deleteAlphaChannelImage (sourceImage, singleChannelImage); } } - bool clipRegionIntersects (int x, int y, int w, int h) + bool clipRegionIntersects (const Rectangle& r) { - return getClipBounds().intersects (Rectangle (x, y, w, h)); + return getClipBounds().intersects (r); } const Rectangle getClipBounds() const @@ -176,11 +183,35 @@ public: return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); } + //============================================================================== + void saveState() + { + CGContextSaveGState (context); + stateStack.add (new SavedState (*state)); + } + + void restoreState() + { + CGContextRestoreGState (context); + + SavedState* const top = stateStack.getLast(); + + if (top != 0) + { + delete state; + state = top; + stateStack.removeLast (1, false); + } + else + { + jassertfalse // trying to pop with an empty stack! + } + } + //============================================================================== 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,38 +242,48 @@ 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 + else if (state->fillType.isGradient()) { CGContextSaveGState (context); - CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + CGContextClipToRect (context, cgRect); flip(); drawGradient(); CGContextRestoreGState (context); } + else + { + CGContextSaveGState (context); + 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) + void drawImage (const Image& sourceImage, const Rectangle& srcClip, + const AffineTransform& transform, const bool fillEntireClipAsTiles) { - Image* singleChannelImage = createAlphaChannelImage (alphaImage); - CGImageRef image = createImage (*singleChannelImage, true); + CGImageRef fullImage = createImage (sourceImage, false); + CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClip.getX(), sourceImage.getHeight() - srcClip.getBottom(), + srcClip.getWidth(), srcClip.getHeight())); + CGImageRelease (fullImage); 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); + CGContextSetAlpha (context, state->fillType.colour.getFloatAlpha()); - CGContextRestoreGState (context); + flip(); + applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + CGRect imageRect = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); - CGImageRelease (image); - deleteAlphaChannelImage (alphaImage, singleChannelImage); - } + 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(); - //============================================================================== - void blendImage (const Image& sourceImage, - int destX, int destY, int destW, int destH, int sourceX, int sourceY) - { - CGImageRef image = createImage (sourceImage, false); + int x = 0, y = 0; + while (x > clip.origin.x) x -= iw; + while (y > clip.origin.y) y -= ih; - 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); + const int right = clip.origin.x + clip.size.width; + const int bottom = clip.origin.y + clip.size.height; - CGContextRestoreGState (context); - CGImageRelease (image); - } + while (y < bottom) + { + for (int x2 = x; x2 < right; x2 += iw) + CGContextDrawImage (context, CGRectMake (x2, y, iw, ih), image); - void blendImageWarping (const Image& sourceImage, - int srcClipX, int srcClipY, int srcClipW, int srcClipH, - const AffineTransform& transform) - { - CGImageRef fullImage = createImage (sourceImage, false); - CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), - srcClipW, srcClipH)); - CGImageRelease (fullImage); - - CGContextSaveGState (context); - flip(); - applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); - - CGContextSetAlpha (context, state->colour.getFloatAlpha()); - CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), - sourceImage.getHeight()), 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);