diff --git a/modules/juce_graphics/native/juce_RenderingHelpers.h b/modules/juce_graphics/native/juce_RenderingHelpers.h index c69124a8a0..f481d71873 100644 --- a/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -1537,7 +1537,8 @@ namespace EdgeTableFillers } //============================================================================== -namespace ClipRegions +template +struct ClipRegions { class Base : public SingleThreadedReferenceCountedObject { @@ -1555,18 +1556,18 @@ namespace ClipRegions virtual Ptr excludeClipRectangle (const Rectangle&) = 0; virtual Ptr clipToPath (const Path&, const AffineTransform&) = 0; virtual Ptr clipToEdgeTable (const EdgeTable& et) = 0; - virtual Ptr clipToImageAlpha (const Image&, const AffineTransform&, const Graphics::ResamplingQuality quality) = 0; + virtual Ptr clipToImageAlpha (const Image&, const AffineTransform&, const Graphics::ResamplingQuality) = 0; virtual void translate (Point delta) = 0; virtual bool clipRegionIntersects (const Rectangle&) const = 0; virtual Rectangle getClipBounds() const = 0; - virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle&, const PixelARGB colour, bool replaceContents) const = 0; - virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle&, const PixelARGB colour) const = 0; - virtual void fillAllWithColour (Image::BitmapData& destData, const PixelARGB colour, bool replaceContents) const = 0; - virtual void fillAllWithGradient (Image::BitmapData& destData, ColourGradient&, const AffineTransform&, bool isIdentity) const = 0; - virtual void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform&, Graphics::ResamplingQuality, bool tiledFill) const = 0; - virtual void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const = 0; + virtual void fillRectWithColour (SavedStateType&, const Rectangle&, const PixelARGB colour, bool replaceContents) const = 0; + virtual void fillRectWithColour (SavedStateType&, const Rectangle&, const PixelARGB colour) const = 0; + virtual void fillAllWithColour (SavedStateType&, const PixelARGB colour, bool replaceContents) const = 0; + virtual void fillAllWithGradient (SavedStateType&, ColourGradient&, const AffineTransform&, bool isIdentity) const = 0; + virtual void renderImageTransformed (SavedStateType&, const Image&, const int alpha, const AffineTransform&, Graphics::ResamplingQuality, bool tiledFill) const = 0; + virtual void renderImageUntransformed (SavedStateType&, const Image&, const int alpha, int x, int y, bool tiledFill) const = 0; }; //============================================================================== @@ -1580,16 +1581,18 @@ namespace ClipRegions EdgeTableRegion (const Rectangle& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {} EdgeTableRegion (const EdgeTableRegion& other) : Base(), edgeTable (other.edgeTable) {} - Ptr clone() const { return new EdgeTableRegion (*this); } - Ptr applyClipTo (const Ptr& target) const { return target->clipToEdgeTable (edgeTable); } + typedef typename Base::Ptr Ptr; - Ptr clipToRectangle (const Rectangle& r) + Ptr clone() const override { return new EdgeTableRegion (*this); } + Ptr applyClipTo (const Ptr& target) const override { return target->clipToEdgeTable (edgeTable); } + + Ptr clipToRectangle (const Rectangle& r) override { edgeTable.clipToRectangle (r); return edgeTable.isEmpty() ? nullptr : this; } - Ptr clipToRectangleList (const RectangleList& r) + Ptr clipToRectangleList (const RectangleList& r) override { RectangleList inverse (edgeTable.getMaximumBounds()); @@ -1600,26 +1603,26 @@ namespace ClipRegions return edgeTable.isEmpty() ? nullptr : this; } - Ptr excludeClipRectangle (const Rectangle& r) + Ptr excludeClipRectangle (const Rectangle& r) override { edgeTable.excludeRectangle (r); return edgeTable.isEmpty() ? nullptr : this; } - Ptr clipToPath (const Path& p, const AffineTransform& transform) + Ptr clipToPath (const Path& p, const AffineTransform& transform) override { EdgeTable et (edgeTable.getMaximumBounds(), p, transform); edgeTable.clipToEdgeTable (et); return edgeTable.isEmpty() ? nullptr : this; } - Ptr clipToEdgeTable (const EdgeTable& et) + Ptr clipToEdgeTable (const EdgeTable& et) override { edgeTable.clipToEdgeTable (et); return edgeTable.isEmpty() ? nullptr : this; } - Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const Graphics::ResamplingQuality quality) + Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const Graphics::ResamplingQuality quality) override { const Image::BitmapData srcData (image, Image::BitmapData::readOnly); @@ -1679,7 +1682,7 @@ namespace ClipRegions return edgeTable.getMaximumBounds(); } - void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB colour, bool replaceContents) const + void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour, bool replaceContents) const override { const Rectangle totalClip (edgeTable.getMaximumBounds()); const Rectangle clipped (totalClip.getIntersection (area)); @@ -1688,11 +1691,11 @@ namespace ClipRegions { EdgeTableRegion et (clipped); et.edgeTable.clipToEdgeTable (edgeTable); - et.fillAllWithColour (destData, colour, replaceContents); + state.fillWithSolidColour (et.edgeTable, colour, replaceContents); } } - void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB colour) const + void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour) const override { const Rectangle totalClip (edgeTable.getMaximumBounds().toFloat()); const Rectangle clipped (totalClip.getIntersection (area)); @@ -1701,48 +1704,33 @@ namespace ClipRegions { EdgeTableRegion et (clipped); et.edgeTable.clipToEdgeTable (edgeTable); - et.fillAllWithColour (destData, colour, false); + state.fillWithSolidColour (et.edgeTable, colour, false); } } - void fillAllWithColour (Image::BitmapData& destData, const PixelARGB colour, bool replaceContents) const + void fillAllWithColour (SavedStateType& state, const PixelARGB colour, bool replaceContents) const override { - switch (destData.pixelFormat) - { - case Image::ARGB: EdgeTableFillers::renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelARGB*) 0); break; - case Image::RGB: EdgeTableFillers::renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelRGB*) 0); break; - default: EdgeTableFillers::renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelAlpha*) 0); break; - } + state.fillWithSolidColour (edgeTable, colour, replaceContents); } - void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const + void fillAllWithGradient (SavedStateType& state, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const override { - HeapBlock lookupTable; - const int numLookupEntries = gradient.createLookupTable (transform, lookupTable); - jassert (numLookupEntries > 0); - - switch (destData.pixelFormat) - { - case Image::ARGB: EdgeTableFillers::renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; - case Image::RGB: EdgeTableFillers::renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; - default: EdgeTableFillers::renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; - } + state.fillWithGradient (edgeTable, gradient, transform, isIdentity); } - void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const + void renderImageTransformed (SavedStateType& state, const Image& src, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const override { - EdgeTableFillers::renderImageTransformed (edgeTable, destData, srcData, alpha, transform, quality, tiledFill); + state.renderImageTransformed (edgeTable, src, alpha, transform, quality, tiledFill); } - void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const + void renderImageUntransformed (SavedStateType& state, const Image& src, const int alpha, int x, int y, bool tiledFill) const override { - EdgeTableFillers::renderImageUntransformed (edgeTable, destData, srcData, alpha, x, y, tiledFill); + state.renderImageUntransformed (edgeTable, src, alpha, x, y, tiledFill); } EdgeTable edgeTable; private: - //============================================================================== template void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const Graphics::ResamplingQuality quality, const SrcPixelType*) { @@ -1776,31 +1764,33 @@ namespace ClipRegions RectangleListRegion (const RectangleList& r) : clip (r) {} RectangleListRegion (const RectangleListRegion& other) : Base(), clip (other.clip) {} - Ptr clone() const { return new RectangleListRegion (*this); } - Ptr applyClipTo (const Ptr& target) const { return target->clipToRectangleList (clip); } + typedef typename Base::Ptr Ptr; - Ptr clipToRectangle (const Rectangle& r) + Ptr clone() const override { return new RectangleListRegion (*this); } + Ptr applyClipTo (const Ptr& target) const override { return target->clipToRectangleList (clip); } + + Ptr clipToRectangle (const Rectangle& r) override { clip.clipTo (r); return clip.isEmpty() ? nullptr : this; } - Ptr clipToRectangleList (const RectangleList& r) + Ptr clipToRectangleList (const RectangleList& r) override { clip.clipTo (r); return clip.isEmpty() ? nullptr : this; } - Ptr excludeClipRectangle (const Rectangle& r) + Ptr excludeClipRectangle (const Rectangle& r) override { clip.subtract (r); return clip.isEmpty() ? nullptr : this; } - Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toEdgeTable()->clipToPath (p, transform); } - Ptr clipToEdgeTable (const EdgeTable& et) { return toEdgeTable()->clipToEdgeTable (et); } + Ptr clipToPath (const Path& p, const AffineTransform& transform) override { return toEdgeTable()->clipToPath (p, transform); } + Ptr clipToEdgeTable (const EdgeTable& et) override { return toEdgeTable()->clipToEdgeTable (et); } - Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const Graphics::ResamplingQuality quality) + Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const Graphics::ResamplingQuality quality) override { return toEdgeTable()->clipToImageAlpha (image, transform, quality); } @@ -1813,62 +1803,36 @@ namespace ClipRegions bool clipRegionIntersects (const Rectangle& r) const { return clip.intersects (r); } Rectangle getClipBounds() const { return clip.getBounds(); } - void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB colour, bool replaceContents) const + void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour, bool replaceContents) const override { SubRectangleIterator iter (clip, area); - - switch (destData.pixelFormat) - { - case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break; - case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break; - default: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break; - } + state.fillWithSolidColour (iter, colour, replaceContents); } - void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB colour) const + void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour) const override { SubRectangleIteratorFloat iter (clip, area); - - switch (destData.pixelFormat) - { - case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, false, (PixelARGB*) 0); break; - case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, false, (PixelRGB*) 0); break; - default: EdgeTableFillers::renderSolidFill (iter, destData, colour, false, (PixelAlpha*) 0); break; - } + state.fillWithSolidColour (iter, colour, false); } - void fillAllWithColour (Image::BitmapData& destData, const PixelARGB colour, bool replaceContents) const + void fillAllWithColour (SavedStateType& state, const PixelARGB colour, bool replaceContents) const override { - switch (destData.pixelFormat) - { - case Image::ARGB: EdgeTableFillers::renderSolidFill (*this, destData, colour, replaceContents, (PixelARGB*) 0); break; - case Image::RGB: EdgeTableFillers::renderSolidFill (*this, destData, colour, replaceContents, (PixelRGB*) 0); break; - default: EdgeTableFillers::renderSolidFill (*this, destData, colour, replaceContents, (PixelAlpha*) 0); break; - } + state.fillWithSolidColour (*this, colour, replaceContents); } - void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const + void fillAllWithGradient (SavedStateType& state, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const override { - HeapBlock lookupTable; - const int numLookupEntries = gradient.createLookupTable (transform, lookupTable); - jassert (numLookupEntries > 0); - - switch (destData.pixelFormat) - { - case Image::ARGB: EdgeTableFillers::renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; - case Image::RGB: EdgeTableFillers::renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; - default: EdgeTableFillers::renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; - } + state.fillWithGradient (*this, gradient, transform, isIdentity); } - void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const + void renderImageTransformed (SavedStateType& state, const Image& src, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const override { - EdgeTableFillers::renderImageTransformed (*this, destData, srcData, alpha, transform, quality, tiledFill); + state.renderImageTransformed (*this, src, alpha, transform, quality, tiledFill); } - void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const + void renderImageUntransformed (SavedStateType& state, const Image& src, const int alpha, int x, int y, bool tiledFill) const override { - EdgeTableFillers::renderImageUntransformed (*this, destData, srcData, alpha, x, y, tiledFill); + state.renderImageUntransformed (*this, src, alpha, x, y, tiledFill); } RectangleList clip; @@ -2020,40 +1984,42 @@ namespace ClipRegions JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat) }; - inline Ptr toEdgeTable() const { return new EdgeTableRegion (clip); } + Ptr toEdgeTable() const { return new EdgeTableRegion (clip); } RectangleListRegion& operator= (const RectangleListRegion&); }; -} +}; //============================================================================== -class SoftwareRendererSavedState +template +class SavedStateBase { public: - SoftwareRendererSavedState (const Image& im, const Rectangle& clipBounds) - : image (im), clip (new ClipRegions::RectangleListRegion (clipBounds)), - transform (0, 0), - interpolationQuality (Graphics::mediumResamplingQuality), - transparencyLayerAlpha (1.0f) + typedef typename ClipRegions::Base BaseRegionType; + typedef typename ClipRegions::EdgeTableRegion EdgeTableRegionType; + typedef typename ClipRegions::RectangleListRegion RectangleListRegionType; + + SavedStateBase (const Rectangle& initialClip) + : clip (new RectangleListRegionType (initialClip)), transform (0, 0), + interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f) { } - SoftwareRendererSavedState (const Image& im, const RectangleList& clipList, const int x, const int y) - : image (im), clip (new ClipRegions::RectangleListRegion (clipList)), - transform (x, y), - interpolationQuality (Graphics::mediumResamplingQuality), - transparencyLayerAlpha (1.0f) + SavedStateBase (const RectangleList& clipList, int x, int y) + : clip (new RectangleListRegionType (clipList)), transform (x, y), + interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f) { } - SoftwareRendererSavedState (const SoftwareRendererSavedState& other) - : image (other.image), clip (other.clip), transform (other.transform), - font (other.font), fillType (other.fillType), + SavedStateBase (const SavedStateBase& other) + : clip (other.clip), transform (other.transform), fillType (other.fillType), interpolationQuality (other.interpolationQuality), transparencyLayerAlpha (other.transparencyLayerAlpha) { } + SavedStateType& getThis() noexcept { return *static_cast (this); } + bool clipToRectangle (const Rectangle& r) { if (clip != nullptr) @@ -2183,39 +2149,6 @@ public: : Rectangle(); } - SoftwareRendererSavedState* beginTransparencyLayer (float opacity) - { - SoftwareRendererSavedState* s = new SoftwareRendererSavedState (*this); - - if (clip != nullptr) - { - const Rectangle layerBounds (clip->getClipBounds()); - - s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true); - s->transparencyLayerAlpha = opacity; - s->transform.moveOriginInDeviceSpace (-layerBounds.getX(), -layerBounds.getY()); - - s->cloneClipIfMultiplyReferenced(); - s->clip->translate (-layerBounds.getPosition()); - } - - return s; - } - - void endTransparencyLayer (SoftwareRendererSavedState& finishedLayerState) - { - if (clip != nullptr) - { - const Rectangle layerBounds (clip->getClipBounds()); - - const ScopedPointer g (image.createLowLevelContext()); - g->setOpacity (finishedLayerState.transparencyLayerAlpha); - g->drawImage (finishedLayerState.image, AffineTransform::translation ((float) layerBounds.getX(), - (float) layerBounds.getY())); - } - } - - //============================================================================== void setFillType (const FillType& newFill) { fillType = newFill; @@ -2225,15 +2158,14 @@ public: { if (fillType.isColour()) { - Image::BitmapData destData (image, Image::BitmapData::readWrite); - clip->fillRectWithColour (destData, r, fillType.colour.getPixelARGB(), replaceContents); + clip->fillRectWithColour (getThis(), r, fillType.colour.getPixelARGB(), replaceContents); } else { const Rectangle clipped (clip->getClipBounds().getIntersection (r)); if (! clipped.isEmpty()) - fillShape (new ClipRegions::RectangleListRegion (clipped), false); + fillShape (new RectangleListRegionType (clipped), false); } } @@ -2241,15 +2173,14 @@ public: { if (fillType.isColour()) { - Image::BitmapData destData (image, Image::BitmapData::readWrite); - clip->fillRectWithColour (destData, r, fillType.colour.getPixelARGB()); + clip->fillRectWithColour (getThis(), r, fillType.colour.getPixelARGB()); } else { const Rectangle clipped (clip->getClipBounds().toFloat().getIntersection (r)); if (! clipped.isEmpty()) - fillShape (new ClipRegions::EdgeTableRegion (clipped), false); + fillShape (new EdgeTableRegionType (clipped), false); } } @@ -2290,7 +2221,7 @@ public: void fillPath (const Path& path, const AffineTransform& t) { if (clip != nullptr) - fillShape (new ClipRegions::EdgeTableRegion (clip->getClipBounds(), path, transform.getTransformWith (t)), false); + fillShape (new EdgeTableRegionType (clip->getClipBounds(), path, transform.getTransformWith (t)), false); } void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y) @@ -2299,43 +2230,83 @@ public: if (clip != nullptr) { - ClipRegions::EdgeTableRegion* edgeTableClip = new ClipRegions::EdgeTableRegion (edgeTable); + EdgeTableRegionType* edgeTableClip = new EdgeTableRegionType (edgeTable); edgeTableClip->edgeTable.translate (x + transform.xOffset, y + transform.yOffset); fillShape (edgeTableClip, false); } } - void drawGlyph (int glyphNumber, const AffineTransform& trans) + void drawLine (const Line & line) { - if (trans.isOnlyTranslation() && transform.isOnlyTranslated) + Path p; + p.addLineSegment (line, 1.0f); + fillPath (p, AffineTransform::identity); + } + + void drawImage (const Image& sourceImage, const AffineTransform& trans) + { + if (clip != nullptr && ! fillType.colour.isTransparent()) + renderImage (sourceImage, trans, nullptr); + } + + void renderImage (const Image& sourceImage, const AffineTransform& trans, + const BaseRegionType* const tiledFillClipRegion) + { + const AffineTransform t (transform.getTransformWith (trans)); + + const int alpha = fillType.colour.getAlpha(); + + if (t.isOnlyTranslation()) { - GlyphCache , SoftwareRendererSavedState>::getInstance() - .drawGlyph (*this, font, glyphNumber, - trans.getTranslationX(), - trans.getTranslationY()); + // If our translation doesn't involve any distortion, just use a simple blit.. + int tx = (int) (t.getTranslationX() * 256.0f); + int ty = (int) (t.getTranslationY() * 256.0f); + + if (interpolationQuality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0) + { + tx = ((tx + 128) >> 8); + ty = ((ty + 128) >> 8); + + if (tiledFillClipRegion != nullptr) + { + tiledFillClipRegion->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, true); + } + else + { + Rectangle area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()); + area = area.getIntersection (getThis().getMaximumBounds()); + + if (! area.isEmpty()) + if (typename BaseRegionType::Ptr c = clip->applyClipTo (new EdgeTableRegionType (area))) + c->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, false); + } + + return; + } } - else + + if (! t.isSingularity()) { - const float fontHeight = font.getHeight(); - drawGlyph (font, glyphNumber, - AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) - .followedBy (trans)); + if (tiledFillClipRegion != nullptr) + { + tiledFillClipRegion->renderImageTransformed (getThis(), sourceImage, alpha, t, interpolationQuality, true); + } + else + { + Path p; + p.addRectangle (sourceImage.getBounds()); + + typename BaseRegionType::Ptr c (clip->clone()); + c = c->clipToPath (p, t); + + if (c != nullptr) + c->renderImageTransformed (getThis(), sourceImage, alpha, t, interpolationQuality, false); + } } } - void drawGlyph (const Font& f, int glyphNumber, const AffineTransform& t) - { - if (clip != nullptr) - { - const ScopedPointer et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, transform.getTransformWith (t))); - - if (et != nullptr) - fillShape (new ClipRegions::EdgeTableRegion (*et), false); - } - } - - void fillShape (ClipRegions::Base::Ptr shapeToFill, const bool replaceContents) + void fillShape (typename BaseRegionType::Ptr shapeToFill, const bool replaceContents) { jassert (clip != nullptr); @@ -2343,8 +2314,6 @@ public: if (shapeToFill != nullptr) { - Image::BitmapData destData (image, Image::BitmapData::readWrite); - if (fillType.isGradient()) { jassert (! replaceContents); // that option is just for solid colours @@ -2363,7 +2332,7 @@ public: t = AffineTransform::identity; } - shapeToFill->fillAllWithGradient (destData, g2, t, isIdentity); + shapeToFill->fillAllWithGradient (getThis(), g2, t, isIdentity); } else if (fillType.isTiledImage()) { @@ -2371,103 +2340,158 @@ public: } else { - shapeToFill->fillAllWithColour (destData, fillType.colour.getPixelARGB(), replaceContents); + shapeToFill->fillAllWithColour (getThis(), fillType.colour.getPixelARGB(), replaceContents); } } } - void drawLine (const Line & line) - { - Path p; - p.addLineSegment (line, 1.0f); - fillPath (p, AffineTransform::identity); - } - - //============================================================================== - void drawImage (const Image& sourceImage, const AffineTransform& trans) - { - renderImage (sourceImage, trans, nullptr); - } - - void renderImage (const Image& sourceImage, const AffineTransform& trans, - const ClipRegions::Base* const tiledFillClipRegion) - { - const AffineTransform t (transform.getTransformWith (trans)); - - const Image::BitmapData destData (image, Image::BitmapData::readWrite); - const Image::BitmapData srcData (sourceImage, Image::BitmapData::readOnly); - const int alpha = fillType.colour.getAlpha(); - - if (t.isOnlyTranslation()) - { - // If our translation doesn't involve any distortion, just use a simple blit.. - int tx = (int) (t.getTranslationX() * 256.0f); - int ty = (int) (t.getTranslationY() * 256.0f); - - if (interpolationQuality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0) - { - tx = ((tx + 128) >> 8); - ty = ((ty + 128) >> 8); - - if (tiledFillClipRegion != nullptr) - { - tiledFillClipRegion->renderImageUntransformed (destData, srcData, alpha, tx, ty, true); - } - else - { - Rectangle area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()); - area = area.getIntersection (image.getBounds()); - - if (! area.isEmpty()) - { - ClipRegions::Base::Ptr c (clip->applyClipTo (new ClipRegions::EdgeTableRegion (area))); - - if (c != nullptr) - c->renderImageUntransformed (destData, srcData, alpha, tx, ty, false); - } - } - - return; - } - } - - if (! t.isSingularity()) - { - if (tiledFillClipRegion != nullptr) - { - tiledFillClipRegion->renderImageTransformed (destData, srcData, alpha, t, interpolationQuality, true); - } - else - { - Path p; - p.addRectangle (sourceImage.getBounds()); - - ClipRegions::Base::Ptr c (clip->clone()); - c = c->clipToPath (p, t); - - if (c != nullptr) - c->renderImageTransformed (destData, srcData, alpha, t, interpolationQuality, false); - } - } - } - - //============================================================================== - Image image; - ClipRegions::Base::Ptr clip; - RenderingHelpers::TranslationOrTransform transform; - Font font; - FillType fillType; - Graphics::ResamplingQuality interpolationQuality; - -private: - float transparencyLayerAlpha; - void cloneClipIfMultiplyReferenced() { if (clip->getReferenceCount() > 1) clip = clip->clone(); } + typename BaseRegionType::Ptr clip; + RenderingHelpers::TranslationOrTransform transform; + FillType fillType; + Graphics::ResamplingQuality interpolationQuality; + float transparencyLayerAlpha; +}; + +//============================================================================== +class SoftwareRendererSavedState : public SavedStateBase +{ + typedef SavedStateBase BaseClass; + +public: + SoftwareRendererSavedState (const Image& im, const Rectangle& clipBounds) + : BaseClass (clipBounds), image (im) + { + } + + SoftwareRendererSavedState (const Image& im, const RectangleList& clipList, int x, int y) + : BaseClass (clipList, x, y), image (im) + { + } + + SoftwareRendererSavedState (const SoftwareRendererSavedState& other) + : BaseClass (other), image (other.image), font (other.font) + { + } + + SoftwareRendererSavedState* beginTransparencyLayer (float opacity) + { + SoftwareRendererSavedState* s = new SoftwareRendererSavedState (*this); + + if (clip != nullptr) + { + const Rectangle layerBounds (clip->getClipBounds()); + + s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true); + s->transparencyLayerAlpha = opacity; + s->transform.moveOriginInDeviceSpace (-layerBounds.getX(), -layerBounds.getY()); + + s->cloneClipIfMultiplyReferenced(); + s->clip->translate (-layerBounds.getPosition()); + } + + return s; + } + + void endTransparencyLayer (SoftwareRendererSavedState& finishedLayerState) + { + if (clip != nullptr) + { + const Rectangle layerBounds (clip->getClipBounds()); + + const ScopedPointer g (image.createLowLevelContext()); + g->setOpacity (finishedLayerState.transparencyLayerAlpha); + g->drawImage (finishedLayerState.image, AffineTransform::translation ((float) layerBounds.getX(), + (float) layerBounds.getY())); + } + } + + //============================================================================== + void drawGlyph (int glyphNumber, const AffineTransform& trans) + { + if (clip != nullptr) + { + if (trans.isOnlyTranslation() && transform.isOnlyTranslated) + { + GlyphCache , SoftwareRendererSavedState>::getInstance() + .drawGlyph (*this, font, glyphNumber, + trans.getTranslationX(), + trans.getTranslationY()); + } + else + { + const float fontHeight = font.getHeight(); + + AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) + .followedBy (trans))); + + const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t)); + + if (et != nullptr) + fillShape (new EdgeTableRegionType (*et), false); + } + } + } + + Rectangle getMaximumBounds() const { return image.getBounds(); } + + //============================================================================== + template + void renderImageTransformed (IteratorType& iter, const Image& src, const int alpha, const AffineTransform& trans, Graphics::ResamplingQuality quality, bool tiledFill) const + { + Image::BitmapData destData (image, Image::BitmapData::readWrite); + const Image::BitmapData srcData (src, Image::BitmapData::readOnly); + EdgeTableFillers::renderImageTransformed (iter, destData, srcData, alpha, trans, quality, tiledFill); + } + + template + void renderImageUntransformed (IteratorType& iter, const Image& src, const int alpha, int x, int y, bool tiledFill) const + { + Image::BitmapData destData (image, Image::BitmapData::readWrite); + const Image::BitmapData srcData (src, Image::BitmapData::readOnly); + EdgeTableFillers::renderImageUntransformed (iter, destData, srcData, alpha, x, y, tiledFill); + } + + template + void fillWithSolidColour (IteratorType& iter, const PixelARGB colour, bool replaceContents) const + { + Image::BitmapData destData (image, Image::BitmapData::readWrite); + + switch (destData.pixelFormat) + { + case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break; + case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break; + default: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break; + } + } + + template + void fillWithGradient (IteratorType& iter, ColourGradient& gradient, const AffineTransform& trans, bool isIdentity) const + { + HeapBlock lookupTable; + const int numLookupEntries = gradient.createLookupTable (trans, lookupTable); + jassert (numLookupEntries > 0); + + Image::BitmapData destData (image, Image::BitmapData::readWrite); + + switch (destData.pixelFormat) + { + case Image::ARGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; + case Image::RGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; + default: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; + } + } + + //============================================================================== + Image image; + Font font; + +private: SoftwareRendererSavedState& operator= (const SoftwareRendererSavedState&); }; diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp index 4abb68c187..00dc417ecc 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp @@ -37,7 +37,7 @@ public: shadersAvailable (false), #endif hasInitialised (false), - needsUpdate (1) + needsUpdate (1), lastMMLockReleaseTime (0) { nativeContext = new NativeContext (component, pixFormat, contextToShare, c.useMultisampling); @@ -83,7 +83,7 @@ public: void invalidate (const Rectangle& area) override { - validArea.subtract (area); + validArea.subtract (area * scale); triggerRepaint(); } @@ -119,7 +119,7 @@ public: return true; } - void clearRegionInFrameBuffer (const RectangleList& list, const float scaleFactor) + void clearRegionInFrameBuffer (const RectangleList& list) { glClearColor (0, 0, 0, 0); glEnable (GL_SCISSOR_TEST); @@ -130,8 +130,7 @@ public: for (const Rectangle* i = list.begin(), * const e = list.end(); i != e; ++i) { - const Rectangle r ((i->toFloat() * scaleFactor).getSmallestIntegerContainer()); - glScissor (r.getX(), imageH - r.getBottom(), r.getWidth(), r.getHeight()); + glScissor (i->getX(), imageH - i->getBottom(), i->getWidth(), i->getHeight()); glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } @@ -148,6 +147,10 @@ public: if (context.renderComponents && isUpdating) { + // This avoids hogging the message thread when doing intensive rendering. + if (lastMMLockReleaseTime + 1 >= Time::getMillisecondCounter()) + Thread::sleep (2); + mmLock = new MessageManagerLock (this); // need to acquire this before locking the context. if (! mmLock->lockWasGained()) return false; @@ -173,6 +176,7 @@ public: { paintComponent(); mmLock = nullptr; + lastMMLockReleaseTime = Time::getMillisecondCounter(); } glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight()); @@ -188,8 +192,7 @@ public: const double newScale = Desktop::getInstance().getDisplays() .getDisplayContaining (component.getScreenBounds().getCentre()).scale; - Rectangle newArea (roundToInt (component.getWidth() * newScale), - roundToInt (component.getHeight() * newScale)); + Rectangle newArea (component.getLocalBounds() * newScale); if (scale != newScale || viewportArea != newArea) { @@ -217,12 +220,12 @@ public: if (! invalid.isEmpty()) { - clearRegionInFrameBuffer (invalid, (float) scale); + clearRegionInFrameBuffer (invalid); { ScopedPointer g (createOpenGLGraphicsContext (context, cachedImageFrameBuffer)); - g->addTransform (AffineTransform::scale ((float) scale)); g->clipToRectangleList (invalid); + g->addTransform (AffineTransform::scale ((float) scale)); paintOwner (*g); JUCE_CHECK_OPENGL_ERROR @@ -371,6 +374,7 @@ public: WaitableEvent canPaintNowFlag, finishedPaintingFlag; bool shadersAvailable, hasInitialised; Atomic needsUpdate; + uint32 lastMMLockReleaseTime; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage) }; diff --git a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp index dc069caad0..4d0c0dbe23 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLGraphicsContext.cpp @@ -27,8 +27,8 @@ namespace OpenGLRendering struct Target { - Target (OpenGLContext& c, GLuint frameBufferID_, int width, int height) noexcept - : context (c), frameBufferID (frameBufferID_), bounds (width, height) + Target (OpenGLContext& c, GLuint fbID, int width, int height) noexcept + : context (c), frameBufferID (fbID), bounds (width, height) {} Target (OpenGLContext& c, OpenGLFrameBuffer& fb, Point origin) noexcept @@ -995,7 +995,8 @@ struct StateHelpers } } - void add (const EdgeTable& et, const PixelARGB colour) + template + void add (const IteratorType& et, const PixelARGB colour) { EdgeTableRenderer etr (*this, colour); et.iterate (etr); @@ -1134,8 +1135,8 @@ public: void flush() { - currentShader.clearShader (shaderQuadQueue); shaderQuadQueue.flush(); + currentShader.clearShader (shaderQuadQueue); JUCE_CHECK_OPENGL_ERROR } @@ -1167,7 +1168,8 @@ public: textureCache.bindTextureForGradient (activeTextures, g); } - const AffineTransform t (transform.translated ((float) -target.bounds.getX(), (float) -target.bounds.getY())); + const AffineTransform t (transform.translated (0.5f - target.bounds.getX(), + 0.5f - target.bounds.getY())); Point p1 (g.point1.transformedBy (t)); const Point p2 (g.point2.transformedBy (t)); const Point p3 (Point (g.point1.x + (g.point2.y - g.point1.y), @@ -1308,590 +1310,21 @@ private: }; //============================================================================== -class ClipRegionBase : public SingleThreadedReferenceCountedObject +class SavedState : public RenderingHelpers::SavedStateBase { -public: - ClipRegionBase (GLState& s) noexcept : state (s) {} - virtual ~ClipRegionBase() {} + typedef RenderingHelpers::SavedStateBase BaseClass; - typedef ReferenceCountedObjectPtr Ptr; - - virtual Ptr clone() const = 0; - virtual Ptr clipToRectangle (const Rectangle&) = 0; - virtual Ptr clipToRectangleList (const RectangleList&) = 0; - virtual Ptr excludeClipRectangle (const Rectangle&) = 0; - virtual Ptr clipToPath (const Path& p, const AffineTransform&) = 0; - virtual Ptr clipToImageAlpha (const OpenGLTextureFromImage&, const AffineTransform&) = 0; - virtual Ptr clipToTexture (const PositionedTexture&) = 0; - virtual Rectangle getClipBounds() const = 0; - virtual void fillRect (const Rectangle& area, const FillType&, bool replaceContents) = 0; - virtual void fillRect (const Rectangle& area, const FillType&) = 0; - virtual void fillEdgeTable (EdgeTable& et, const FillType& fill) = 0; - virtual void drawImage (const Image&, const AffineTransform&, float alpha, - const Rectangle& clip, EdgeTable* mask) = 0; - - GLState& state; - - JUCE_DECLARE_NON_COPYABLE (ClipRegionBase) -}; - - -//============================================================================== -class ClipRegion_Mask : public ClipRegionBase -{ -public: - ClipRegion_Mask (const ClipRegion_Mask& other) - : ClipRegionBase (other.state), - clip (other.clip), - maskArea (other.clip) - { - OpenGLTargetSaver ts (state.target.context); - state.currentShader.clearShader (state.shaderQuadQueue); - state.shaderQuadQueue.flush(); - state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); - state.activeTextures.clear(); - mask.initialise (state.target.context, maskArea.getWidth(), maskArea.getHeight()); - maskArea.setSize (mask.getWidth(), mask.getHeight()); - makeActive(); - - state.blendMode.disableBlend (state.shaderQuadQueue); - state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); - state.activeTextures.bindTexture (other.mask.getTextureID()); - - state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->copyTexture); - state.currentShader.programs->copyTexture.imageParams.imageTexture.set (0); - state.currentShader.programs->copyTexture.imageParams - .setMatrix (AffineTransform::translation ((float) other.maskArea.getX(), (float) other.maskArea.getY()), - other.maskArea.getWidth(), other.maskArea.getHeight(), 1.0f, 1.0f, - (float) maskArea.getX(), (float) maskArea.getY()); - - state.shaderQuadQueue.add (clip, PixelARGB (0xffffffff)); - state.shaderQuadQueue.flush(); - } - - ClipRegion_Mask (GLState& s, const RectangleList& r) - : ClipRegionBase (s), - clip (r.getBounds()), - maskArea (clip) - { - OpenGLTargetSaver ts (state.target.context); - state.currentShader.clearShader (state.shaderQuadQueue); - state.shaderQuadQueue.flush(); - state.activeTextures.clear(); - mask.initialise (state.target.context, maskArea.getWidth(), maskArea.getHeight()); - maskArea.setSize (mask.getWidth(), mask.getHeight()); - mask.makeCurrentAndClear(); - makeActive(); - state.blendMode.setBlendMode (state.shaderQuadQueue, true); - state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram); - state.shaderQuadQueue.add (r, PixelARGB (0xffffffff)); - state.shaderQuadQueue.flush(); - } - - Ptr clone() const { return new ClipRegion_Mask (*this); } - Rectangle getClipBounds() const { return clip; } - - Ptr clipToRectangle (const Rectangle& r) - { - clip = clip.getIntersection (r); - return clip.isEmpty() ? nullptr : this; - } - - Ptr clipToRectangleList (const RectangleList& r) - { - clip = clip.getIntersection (r.getBounds()); - if (clip.isEmpty()) - return Ptr(); - - RectangleList excluded (clip); - - if (excluded.subtract (r)) - { - if (excluded.getNumRectangles() == 1) - return excludeClipRectangle (excluded.getRectangle (0)); - - OpenGLTargetSaver ts (state.target.context); - makeActive(); - state.blendMode.setBlendMode (state.shaderQuadQueue, true); - state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram); - state.shaderQuadQueue.add (excluded, PixelARGB (0)); - state.shaderQuadQueue.flush(); - } - - return this; - } - - Ptr excludeClipRectangle (const Rectangle& r) - { - if (r.contains (clip)) - return Ptr(); - - OpenGLTargetSaver ts (state.target.context); - makeActive(); - state.blendMode.setBlendMode (state.shaderQuadQueue, true); - state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram); - state.shaderQuadQueue.add (r, PixelARGB (0)); - state.shaderQuadQueue.flush(); - return this; - } - - Ptr clipToPath (const Path& p, const AffineTransform& t) - { - EdgeTable et (clip, p, t); - - if (! et.isEmpty()) - { - OpenGLTargetSaver ts (state.target.context); - state.currentShader.clearShader (state.shaderQuadQueue); - state.shaderQuadQueue.flush(); - state.activeTextures.clear(); - - OpenGLTexture texture; - PositionedTexture pt (texture, et, clip); - return clipToTexture (pt); - } - - return Ptr(); - } - - Ptr clipToTexture (const PositionedTexture& pt) - { - clip = clip.getIntersection (pt.clip); - - if (clip.isEmpty()) - return Ptr(); - - OpenGLTargetSaver ts (state.target.context); - makeActive(); - - state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); - state.activeTextures.bindTexture (pt.textureID); - - state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->maskTexture); - state.currentShader.programs->maskTexture.imageParams.imageTexture.set (0); - state.currentShader.programs->maskTexture.imageParams - .setMatrix (AffineTransform::translation ((float) pt.area.getX(), (float) pt.area.getY()), - pt.area.getWidth(), pt.area.getHeight(), 1.0f, 1.0f, - (float) maskArea.getX(), (float) maskArea.getY()); - - state.blendMode.setBlendFunc (state.shaderQuadQueue, GL_ZERO, GL_SRC_ALPHA); - state.shaderQuadQueue.add (clip, PixelARGB (0xffffffff)); - state.shaderQuadQueue.flush(); - return this; - } - - Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) - { - OpenGLTargetSaver ts (state.target.context); - makeActive(); - state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); - state.activeTextures.bindTexture (image.textureID); - - state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->maskTexture); - state.currentShader.programs->maskTexture.imageParams.imageTexture.set (0); - state.currentShader.programs->maskTexture.imageParams - .setMatrix (transform, image, (float) maskArea.getX(), (float) maskArea.getY()); - - state.shaderQuadQueue.add (clip, PixelARGB (0xffffffff)); - state.shaderQuadQueue.flush(); - return this; - } - - void fillRect (const Rectangle& area, const FillType& fill, bool replaceContents) - { - (void) replaceContents; jassert (! replaceContents); - const Rectangle r (clip.getIntersection (area)); - - if (! r.isEmpty()) - { - ShaderFillOperation fillOp (*this, fill, false); - state.shaderQuadQueue.add (r, fill.colour.getPixelARGB()); - } - } - - void fillRect (const Rectangle& area, const FillType& fill) - { - ShaderFillOperation fillOp (*this, fill, false); - - FloatRectangleRenderer frr (*this, fill); - RenderingHelpers::FloatRectangleRasterisingInfo (area).iterate (frr); - } - - void fillEdgeTable (EdgeTable& et, const FillType& fill) - { - if (et.getMaximumBounds().intersects (clip)) - { - if (! clip.contains (et.getMaximumBounds())) - et.clipToRectangle (clip); - - ShaderFillOperation fillOp (*this, fill, false); - state.shaderQuadQueue.add (et, fill.colour.getPixelARGB()); - } - } - - void drawImage (const Image& image, const AffineTransform& transform, - float alpha, const Rectangle& clipArea, EdgeTable* et) - { - const Rectangle r (clip.getIntersection (clipArea)); - - if (! r.isEmpty()) - { - const PixelARGB colour (Colours::white.withAlpha (alpha).getPixelARGB()); - ShaderFillOperation fillOp (*this, FillType (image, transform), true); - - if (et != nullptr) - { - et->clipToRectangle (r); - - if (! et->isEmpty()) - state.shaderQuadQueue.add (*et, colour); - } - else - { - state.shaderQuadQueue.add (r, colour); - } - } - - state.currentShader.clearShader (state.shaderQuadQueue); - } - -private: - OpenGLFrameBuffer mask; - Rectangle clip, maskArea; - - struct ShaderFillOperation - { - ShaderFillOperation (const ClipRegion_Mask& clipMask, const FillType& fill, const bool clampTiledImages) - : state (clipMask.state) - { - const GLuint maskTextureID = clipMask.mask.getTextureID(); - - if (fill.isColour()) - { - state.blendMode.setPremultipliedBlendingMode (state.shaderQuadQueue); - state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); - state.activeTextures.bindTexture (maskTextureID); - - state.setShader (state.currentShader.programs->solidColourMasked); - state.currentShader.programs->solidColourMasked.maskParams.setBounds (clipMask.maskArea, state.target, 0); - } - else if (fill.isGradient()) - { - state.setShaderForGradientFill (*fill.gradient, fill.transform, maskTextureID, &clipMask.maskArea); - } - else - { - jassert (fill.isTiledImage()); - image = new OpenGLTextureFromImage (fill.image); - state.setShaderForTiledImageFill (*image, fill.transform, maskTextureID, &clipMask.maskArea, clampTiledImages); - } - } - - ~ShaderFillOperation() - { - state.shaderQuadQueue.flush(); - } - - GLState& state; - ScopedPointer image; - - JUCE_DECLARE_NON_COPYABLE (ShaderFillOperation) - }; - - void makeActive() - { - state.shaderQuadQueue.flush(); - state.activeTextures.clear(); - mask.makeCurrentRenderingTarget(); - glViewport (0, 0, maskArea.getWidth(), maskArea.getHeight()); - } - - struct FloatRectangleRenderer - { - FloatRectangleRenderer (ClipRegion_Mask& owner_, const FillType& fill_) noexcept - : owner (owner_), originalColour (fill_.colour.getPixelARGB()) - {} - - void operator() (int x, int y, int w, int h, const int alpha) noexcept - { - if (owner.clip.intersectRectangle (x, y, w, h)) - { - PixelARGB col (originalColour); - col.multiplyAlpha (alpha); - owner.state.shaderQuadQueue.add (x, y, w, h, col); - } - } - - private: - ClipRegion_Mask& owner; - const PixelARGB originalColour; - - JUCE_DECLARE_NON_COPYABLE (FloatRectangleRenderer) - }; - - ClipRegion_Mask& operator= (const ClipRegion_Mask&); -}; - -//============================================================================== -class ClipRegion_RectangleList : public ClipRegionBase -{ -public: - ClipRegion_RectangleList (GLState& s, const Rectangle& r) noexcept - : ClipRegionBase (s), clip (r) - {} - - ClipRegion_RectangleList (GLState& s, const RectangleList& r) noexcept - : ClipRegionBase (s), clip (r) - {} - - Ptr clone() const { return new ClipRegion_RectangleList (state, clip); } - - Ptr clipToTexture (const PositionedTexture& t) { return toMask()->clipToTexture (t); } - Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toMask()->clipToPath (p, transform); } - Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) { return toMask()->clipToImageAlpha (image, transform); } - - void fillRect (const Rectangle& area, const FillType& fill, bool replaceContents) - { - ShaderFillOperation fillOp (*this, fill, replaceContents || fill.colour.isOpaque(), false); - state.shaderQuadQueue.add (clip, area, fill.colour.getPixelARGB()); - } - - void fillRect (const Rectangle& area, const FillType& fill) - { - const PixelARGB colour (fill.colour.getPixelARGB()); - ShaderFillOperation fillOp (*this, fill, false, false); - - for (const Rectangle* i = clip.begin(), * const e = clip.end(); i != e; ++i) - { - const Rectangle r (i->toFloat().getIntersection (area)); - if (! r.isEmpty()) - state.shaderQuadQueue.add (r, colour); - } - } - - void drawImage (const Image& image, const AffineTransform& transform, - float alpha, const Rectangle& clipArea, EdgeTable* et) - { - FillType fill (image, transform); - const PixelARGB colour (Colours::white.withAlpha (alpha).getPixelARGB()); - - ShaderFillOperation fillOp (*this, fill, false, true); - - if (et != nullptr) - { - if (! clip.containsRectangle (et->getMaximumBounds())) - et->clipToEdgeTable (EdgeTable (clip)); - - state.shaderQuadQueue.add (*et, colour); - } - else - { - state.shaderQuadQueue.add (clip, clipArea, colour); - } - - state.currentShader.clearShader (state.shaderQuadQueue); - } - - void fillEdgeTable (EdgeTable& et, const FillType& fill) - { - if (clip.intersects (et.getMaximumBounds())) - { - if (! clip.containsRectangle (et.getMaximumBounds())) - et.clipToEdgeTable (EdgeTable (clip)); - - ShaderFillOperation fillOp (*this, fill, false, true); - state.shaderQuadQueue.add (et, fill.colour.getPixelARGB()); - } - } - - Rectangle getClipBounds() const { return clip.getBounds(); } - Ptr clipToRectangle (const Rectangle& r) { return clip.clipTo (r) ? this : nullptr; } - Ptr clipToRectangleList (const RectangleList& r) { return clip.clipTo (r) ? this : nullptr; } - Ptr excludeClipRectangle (const Rectangle& r) { clip.subtract (r); return clip.isEmpty() ? nullptr : this; } - -private: - RectangleList clip; - - Ptr toMask() const { return new ClipRegion_Mask (state, clip); } - - struct ShaderFillOperation - { - ShaderFillOperation (const ClipRegion_RectangleList& clipList, const FillType& fill, - const bool replaceContents, const bool clampTiledImages) - : state (clipList.state) - { - if (fill.isColour()) - { - state.activeTextures.disableTextures (state.shaderQuadQueue); - state.blendMode.setBlendMode (state.shaderQuadQueue, replaceContents); - state.setShader (state.currentShader.programs->solidColourProgram); - } - else if (fill.isGradient()) - { - state.setShaderForGradientFill (*fill.gradient, fill.transform, 0, nullptr); - } - else - { - jassert (fill.isTiledImage()); - state.shaderQuadQueue.flush(); - image = new OpenGLTextureFromImage (fill.image); - state.setShaderForTiledImageFill (*image, fill.transform, 0, nullptr, clampTiledImages); - } - } - - ~ShaderFillOperation() - { - if (image != nullptr) - state.shaderQuadQueue.flush(); - } - - GLState& state; - ScopedPointer image; - - JUCE_DECLARE_NON_COPYABLE (ShaderFillOperation) - }; - - JUCE_DECLARE_NON_COPYABLE (ClipRegion_RectangleList) -}; - -//============================================================================== -class SavedState -{ public: SavedState (GLState* const s) - : clip (new ClipRegion_RectangleList (*s, s->target.bounds)), - transform (0, 0), interpolationQuality (Graphics::mediumResamplingQuality), - state (s), transparencyLayerAlpha (1.0f) + : BaseClass (s->target.bounds), state (s) {} SavedState (const SavedState& other) - : clip (other.clip), transform (other.transform), font (other.font), - fillType (other.fillType), interpolationQuality (other.interpolationQuality), - state (other.state), transparencyLayerAlpha (other.transparencyLayerAlpha), - transparencyLayer (other.transparencyLayer), previousTarget (other.previousTarget.createCopy()) + : BaseClass (other), font (other.font), + state (other.state), transparencyLayer (other.transparencyLayer), + previousTarget (other.previousTarget.createCopy()) {} - bool clipToRectangle (const Rectangle& r) - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated) - { - cloneClipIfMultiplyReferenced(); - clip = clip->clipToRectangle (transform.translated (r)); - } - else if (transform.isIntegerScaling) - { - cloneClipIfMultiplyReferenced(); - clip = clip->clipToRectangle (transform.transformed (r).getSmallestIntegerContainer()); - } - else - { - Path p; - p.addRectangle (r); - clipToPath (p, AffineTransform::identity); - } - } - - return clip != nullptr; - } - - bool clipToRectangleList (const RectangleList& r) - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated) - { - cloneClipIfMultiplyReferenced(); - RectangleList offsetList (r); - offsetList.offsetAll (transform.xOffset, transform.yOffset); - clip = clip->clipToRectangleList (offsetList); - } - else if (transform.isIntegerScaling) - { - cloneClipIfMultiplyReferenced(); - RectangleList scaledList; - - for (const Rectangle* i = r.begin(), * const e = r.end(); i != e; ++i) - scaledList.add (transform.transformed (*i).getSmallestIntegerContainer()); - - clip = clip->clipToRectangleList (scaledList); - } - else - { - clipToPath (r.toPath(), AffineTransform::identity); - } - } - - return clip != nullptr; - } - - bool excludeClipRectangle (const Rectangle& r) - { - if (clip != nullptr) - { - cloneClipIfMultiplyReferenced(); - - if (transform.isOnlyTranslated) - { - clip = clip->excludeClipRectangle (transform.translated (r)); - } - else if (transform.isIntegerScaling) - { - clip = clip->excludeClipRectangle (transform.transformed (r).getSmallestIntegerContainer()); - } - else - { - Path p; - p.addRectangle (r.toFloat()); - p.applyTransform (transform.complexTransform); - p.addRectangle (clip->getClipBounds().toFloat()); - p.setUsingNonZeroWinding (false); - clip = clip->clipToPath (p, AffineTransform::identity); - } - } - - return clip != nullptr; - } - - void clipToPath (const Path& p, const AffineTransform& t) - { - if (clip != nullptr) - { - cloneClipIfMultiplyReferenced(); - clip = clip->clipToPath (p, transform.getTransformWith (t)); - } - } - - void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t) - { - if (clip != nullptr) - { - Path p; - p.addRectangle (sourceImage.getBounds()); - clipToPath (p, t); - - if (sourceImage.hasAlphaChannel() && clip != nullptr) - { - cloneClipIfMultiplyReferenced(); - clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t)); - } - } - } - - bool clipRegionIntersects (const Rectangle& r) const - { - return clip != nullptr - && (transform.isOnlyTranslated ? clip->getClipBounds().intersects (transform.translated (r)) - : getClipBounds().intersects (r)); - } - - Rectangle getClipBounds() const - { - return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds()) - : Rectangle(); - } - SavedState* beginTransparencyLayer (float opacity) { SavedState* const s = new SavedState (*this); @@ -1926,188 +1359,96 @@ public: state->target.makeActive(); const Rectangle clipBounds (clip->getClipBounds()); - clip->drawImage (finishedLayerState.transparencyLayer, - AffineTransform::translation ((float) clipBounds.getX(), (float) clipBounds.getY()), - finishedLayerState.transparencyLayerAlpha, clipBounds, nullptr); + clip->renderImageUntransformed (*this, finishedLayerState.transparencyLayer, + (int) (finishedLayerState.transparencyLayerAlpha * 255.0f), + clipBounds.getX(), clipBounds.getY(), false); } } - //============================================================================== - void fillRect (const Rectangle& r, const bool replaceContents) + void drawGlyph (int glyphNumber, const AffineTransform& trans) { if (clip != nullptr) { - if (transform.isOnlyTranslated) - { - clip->fillRect (r.translated (transform.xOffset, transform.yOffset), - getFillType(), replaceContents); - } - else - { - Path p; - p.addRectangle (r); - fillPath (p, AffineTransform::identity); - } - } - } - - void fillRect (const Rectangle& r) - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated) - { - const Rectangle c (r.translated ((float) transform.xOffset, (float) transform.yOffset) - .getIntersection (clip->getClipBounds().toFloat())); - - if (! c.isEmpty()) - clip->fillRect (c, getFillType()); - } - else - { - Path p; - p.addRectangle (r); - fillPath (p, AffineTransform::identity); - } - } - } - - void fillPath (const Path& path, const AffineTransform& t) - { - if (clip != nullptr) - { - EdgeTable et (clip->getClipBounds(), path, transform.getTransformWith (t)); - fillEdgeTable (et); - } - } - - void drawGlyph (int glyphNumber, const AffineTransform& t) - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated && t.isOnlyTranslation()) + if (trans.isOnlyTranslation() && transform.isOnlyTranslated) { RenderingHelpers::GlyphCache , SavedState>::getInstance() .drawGlyph (*this, font, glyphNumber, - transform.xOffset + t.getTranslationX(), - transform.yOffset + t.getTranslationY()); + trans.getTranslationX(), + trans.getTranslationY()); } else { const float fontHeight = font.getHeight(); - const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph - (glyphNumber, transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) - .followedBy (t)))); + AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) + .followedBy (trans))); + + const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t)); if (et != nullptr) - fillEdgeTable (*et); + fillShape (new EdgeTableRegionType (*et), false); } } } - void fillEdgeTable (const EdgeTable& et, const float x, const int y) - { - if (clip != nullptr) - { - EdgeTable et2 (et); - et2.translate (x, y); - - fillEdgeTable (et2); - } - } - - void drawLine (const Line & line) - { - Path p; - p.addLineSegment (line, 1.0f); - fillPath (p, AffineTransform::identity); - } - - //============================================================================== - void drawImage (const Image& image, const AffineTransform& trans) - { - if (clip == nullptr || fillType.colour.isTransparent()) - return; - - const AffineTransform t (transform.getTransformWith (trans)); - if (t.isSingularity()) - return; - - const float alpha = fillType.colour.getFloatAlpha(); - - float px0 = 0, py0 = 0; - float px1 = (float) image.getWidth(), py1 = 0; - float px2 = 0, py2 = (float) image.getHeight(); - t.transformPoints (px0, py0, px1, py1, px2, py2); - - const int ix0 = (int) (px0 * 256.0f); - const int iy0 = (int) (py0 * 256.0f); - const int ix1 = (int) (px1 * 256.0f); - const int iy1 = (int) (py1 * 256.0f); - const int ix2 = (int) (px2 * 256.0f); - const int iy2 = (int) (py2 * 256.0f); - - if (((ix0 | iy0 | ix1 | iy1 | ix2 | iy2) & 0xf8) == 0 - && ix0 == ix2 && iy0 == iy1) - { - // Non-warping transform can be done as a single rectangle. - clip->drawImage (image, t, alpha, - Rectangle (Point (((ix0 + 128) >> 8), - ((iy0 + 128) >> 8)), - Point (((ix1 + 128) >> 8), - ((iy2 + 128) >> 8))), nullptr); - } - else - { - Path p; - p.addRectangle (image.getBounds()); - - const Rectangle clipBounds (clip->getClipBounds()); - EdgeTable et (clipBounds, p, t); - - clip->drawImage (image, t, alpha, clipBounds, &et); - } - } + Rectangle getMaximumBounds() const { return state->target.bounds; } void setFillType (const FillType& newFill) { - fillType = newFill; + BaseClass::setFillType (newFill); state->textureCache.resetGradient(); } //============================================================================== - ClipRegionBase::Ptr clip; - RenderingHelpers::TranslationOrTransform transform; + template + void renderImageTransformed (IteratorType& iter, const Image& src, const int alpha, + const AffineTransform& trans, Graphics::ResamplingQuality quality, bool tiledFill) const + { + state->shaderQuadQueue.flush(); + OpenGLTextureFromImage image (src); + state->setShaderForTiledImageFill (image, trans, 0, nullptr, ! tiledFill); + + state->shaderQuadQueue.add (iter, PixelARGB ((uint8) alpha, (uint8) alpha, (uint8) alpha, (uint8) alpha)); + state->shaderQuadQueue.flush(); + + state->currentShader.clearShader (state->shaderQuadQueue); + } + + template + void renderImageUntransformed (IteratorType& iter, const Image& src, const int alpha, int x, int y, bool tiledFill) const + { + renderImageTransformed (iter, src, alpha, AffineTransform::translation ((float) x, (float) y), + Graphics::lowResamplingQuality, tiledFill); + } + + template + void fillWithSolidColour (IteratorType& iter, const PixelARGB colour, bool replaceContents) const + { + state->activeTextures.disableTextures (state->shaderQuadQueue); + state->blendMode.setBlendMode (state->shaderQuadQueue, replaceContents); + state->setShader (state->currentShader.programs->solidColourProgram); + state->shaderQuadQueue.add (iter, colour); + } + + template + void fillWithGradient (IteratorType& iter, ColourGradient& gradient, const AffineTransform& trans, bool isIdentity) const + { + state->setShaderForGradientFill (gradient, trans, 0, nullptr); + state->shaderQuadQueue.add (iter, fillType.colour.getPixelARGB()); + } + + //============================================================================== Font font; - FillType fillType; - Graphics::ResamplingQuality interpolationQuality; GLState* state; private: - float transparencyLayerAlpha; Image transparencyLayer; ScopedPointer previousTarget; - void cloneClipIfMultiplyReferenced() - { - if (clip->getReferenceCount() > 1) - clip = clip->clone(); - } - - FillType getFillType() const - { - return fillType.transformed (transform.getTransform()); - } - - void fillEdgeTable (EdgeTable& et) const - { - clip->fillEdgeTable (et, getFillType()); - } - SavedState& operator= (const SavedState&); }; + //============================================================================== class ShaderContext : public RenderingHelpers::StackBasedLowLevelGraphicsContext {