1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Refactored OpenGLGraphicsContext to share a lot of code with the software renderer, and to use edgetables rather than image masks for faster clipping.

This commit is contained in:
jules 2013-07-30 17:20:58 +01:00
parent 08c9d5d22f
commit 67916bc9d0
3 changed files with 362 additions and 993 deletions

View file

@ -1537,7 +1537,8 @@ namespace EdgeTableFillers
}
//==============================================================================
namespace ClipRegions
template <class SavedStateType>
struct ClipRegions
{
class Base : public SingleThreadedReferenceCountedObject
{
@ -1555,18 +1556,18 @@ namespace ClipRegions
virtual Ptr excludeClipRectangle (const Rectangle<int>&) = 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<int> delta) = 0;
virtual bool clipRegionIntersects (const Rectangle<int>&) const = 0;
virtual Rectangle<int> getClipBounds() const = 0;
virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>&, const PixelARGB colour, bool replaceContents) const = 0;
virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle<float>&, 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<int>&, const PixelARGB colour, bool replaceContents) const = 0;
virtual void fillRectWithColour (SavedStateType&, const Rectangle<float>&, 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<int>& 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<int>& r)
Ptr clone() const override { return new EdgeTableRegion (*this); }
Ptr applyClipTo (const Ptr& target) const override { return target->clipToEdgeTable (edgeTable); }
Ptr clipToRectangle (const Rectangle<int>& r) override
{
edgeTable.clipToRectangle (r);
return edgeTable.isEmpty() ? nullptr : this;
}
Ptr clipToRectangleList (const RectangleList<int>& r)
Ptr clipToRectangleList (const RectangleList<int>& r) override
{
RectangleList<int> inverse (edgeTable.getMaximumBounds());
@ -1600,26 +1603,26 @@ namespace ClipRegions
return edgeTable.isEmpty() ? nullptr : this;
}
Ptr excludeClipRectangle (const Rectangle<int>& r)
Ptr excludeClipRectangle (const Rectangle<int>& 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<int>& area, const PixelARGB colour, bool replaceContents) const
void fillRectWithColour (SavedStateType& state, const Rectangle<int>& area, const PixelARGB colour, bool replaceContents) const override
{
const Rectangle<int> totalClip (edgeTable.getMaximumBounds());
const Rectangle<int> 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<float>& area, const PixelARGB colour) const
void fillRectWithColour (SavedStateType& state, const Rectangle<float>& area, const PixelARGB colour) const override
{
const Rectangle<float> totalClip (edgeTable.getMaximumBounds().toFloat());
const Rectangle<float> 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 <PixelARGB> 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 <class SrcPixelType>
void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const Graphics::ResamplingQuality quality, const SrcPixelType*)
{
@ -1776,31 +1764,33 @@ namespace ClipRegions
RectangleListRegion (const RectangleList<int>& 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<int>& r)
Ptr clone() const override { return new RectangleListRegion (*this); }
Ptr applyClipTo (const Ptr& target) const override { return target->clipToRectangleList (clip); }
Ptr clipToRectangle (const Rectangle<int>& r) override
{
clip.clipTo (r);
return clip.isEmpty() ? nullptr : this;
}
Ptr clipToRectangleList (const RectangleList<int>& r)
Ptr clipToRectangleList (const RectangleList<int>& r) override
{
clip.clipTo (r);
return clip.isEmpty() ? nullptr : this;
}
Ptr excludeClipRectangle (const Rectangle<int>& r)
Ptr excludeClipRectangle (const Rectangle<int>& 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<int>& r) const { return clip.intersects (r); }
Rectangle<int> getClipBounds() const { return clip.getBounds(); }
void fillRectWithColour (Image::BitmapData& destData, const Rectangle<int>& area, const PixelARGB colour, bool replaceContents) const
void fillRectWithColour (SavedStateType& state, const Rectangle<int>& 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<float>& area, const PixelARGB colour) const
void fillRectWithColour (SavedStateType& state, const Rectangle<float>& 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 <PixelARGB> 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<int> 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 SavedStateType>
class SavedStateBase
{
public:
SoftwareRendererSavedState (const Image& im, const Rectangle<int>& clipBounds)
: image (im), clip (new ClipRegions::RectangleListRegion (clipBounds)),
transform (0, 0),
interpolationQuality (Graphics::mediumResamplingQuality),
transparencyLayerAlpha (1.0f)
typedef typename ClipRegions<SavedStateType>::Base BaseRegionType;
typedef typename ClipRegions<SavedStateType>::EdgeTableRegion EdgeTableRegionType;
typedef typename ClipRegions<SavedStateType>::RectangleListRegion RectangleListRegionType;
SavedStateBase (const Rectangle<int>& initialClip)
: clip (new RectangleListRegionType (initialClip)), transform (0, 0),
interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f)
{
}
SoftwareRendererSavedState (const Image& im, const RectangleList<int>& 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<int>& 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 <SavedStateType*> (this); }
bool clipToRectangle (const Rectangle<int>& r)
{
if (clip != nullptr)
@ -2183,39 +2149,6 @@ public:
: Rectangle<int>();
}
SoftwareRendererSavedState* beginTransparencyLayer (float opacity)
{
SoftwareRendererSavedState* s = new SoftwareRendererSavedState (*this);
if (clip != nullptr)
{
const Rectangle<int> 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<int> layerBounds (clip->getClipBounds());
const ScopedPointer<LowLevelGraphicsContext> 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<int> 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<float> 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 <float>& 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 <CachedGlyphEdgeTable <SoftwareRendererSavedState>, 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<int> 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<EdgeTable> 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 <float>& 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<int> 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<SoftwareRendererSavedState>
{
typedef SavedStateBase<SoftwareRendererSavedState> BaseClass;
public:
SoftwareRendererSavedState (const Image& im, const Rectangle<int>& clipBounds)
: BaseClass (clipBounds), image (im)
{
}
SoftwareRendererSavedState (const Image& im, const RectangleList<int>& 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<int> 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<int> layerBounds (clip->getClipBounds());
const ScopedPointer<LowLevelGraphicsContext> 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 <CachedGlyphEdgeTable <SoftwareRendererSavedState>, 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<EdgeTable> et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t));
if (et != nullptr)
fillShape (new EdgeTableRegionType (*et), false);
}
}
}
Rectangle<int> getMaximumBounds() const { return image.getBounds(); }
//==============================================================================
template <typename IteratorType>
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 <typename IteratorType>
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 <typename IteratorType>
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 <typename IteratorType>
void fillWithGradient (IteratorType& iter, ColourGradient& gradient, const AffineTransform& trans, bool isIdentity) const
{
HeapBlock <PixelARGB> 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&);
};

View file

@ -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<int>& area) override
{
validArea.subtract (area);
validArea.subtract (area * scale);
triggerRepaint();
}
@ -119,7 +119,7 @@ public:
return true;
}
void clearRegionInFrameBuffer (const RectangleList<int>& list, const float scaleFactor)
void clearRegionInFrameBuffer (const RectangleList<int>& list)
{
glClearColor (0, 0, 0, 0);
glEnable (GL_SCISSOR_TEST);
@ -130,8 +130,7 @@ public:
for (const Rectangle<int>* i = list.begin(), * const e = list.end(); i != e; ++i)
{
const Rectangle<int> 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<int> newArea (roundToInt (component.getWidth() * newScale),
roundToInt (component.getHeight() * newScale));
Rectangle<int> newArea (component.getLocalBounds() * newScale);
if (scale != newScale || viewportArea != newArea)
{
@ -217,12 +220,12 @@ public:
if (! invalid.isEmpty())
{
clearRegionInFrameBuffer (invalid, (float) scale);
clearRegionInFrameBuffer (invalid);
{
ScopedPointer<LowLevelGraphicsContext> 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<int> needsUpdate;
uint32 lastMMLockReleaseTime;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
};

View file

@ -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<int> origin) noexcept
@ -995,7 +995,8 @@ struct StateHelpers
}
}
void add (const EdgeTable& et, const PixelARGB colour)
template <class IteratorType>
void add (const IteratorType& et, const PixelARGB colour)
{
EdgeTableRenderer<ShaderQuadQueue> 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<float> p1 (g.point1.transformedBy (t));
const Point<float> p2 (g.point2.transformedBy (t));
const Point<float> p3 (Point<float> (g.point1.x + (g.point2.y - g.point1.y),
@ -1308,590 +1310,21 @@ private:
};
//==============================================================================
class ClipRegionBase : public SingleThreadedReferenceCountedObject
class SavedState : public RenderingHelpers::SavedStateBase<SavedState>
{
public:
ClipRegionBase (GLState& s) noexcept : state (s) {}
virtual ~ClipRegionBase() {}
typedef RenderingHelpers::SavedStateBase<SavedState> BaseClass;
typedef ReferenceCountedObjectPtr<ClipRegionBase> Ptr;
virtual Ptr clone() const = 0;
virtual Ptr clipToRectangle (const Rectangle<int>&) = 0;
virtual Ptr clipToRectangleList (const RectangleList<int>&) = 0;
virtual Ptr excludeClipRectangle (const Rectangle<int>&) = 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<int> getClipBounds() const = 0;
virtual void fillRect (const Rectangle<int>& area, const FillType&, bool replaceContents) = 0;
virtual void fillRect (const Rectangle<float>& area, const FillType&) = 0;
virtual void fillEdgeTable (EdgeTable& et, const FillType& fill) = 0;
virtual void drawImage (const Image&, const AffineTransform&, float alpha,
const Rectangle<int>& 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<int>& 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<int> getClipBounds() const { return clip; }
Ptr clipToRectangle (const Rectangle<int>& r)
{
clip = clip.getIntersection (r);
return clip.isEmpty() ? nullptr : this;
}
Ptr clipToRectangleList (const RectangleList<int>& r)
{
clip = clip.getIntersection (r.getBounds());
if (clip.isEmpty())
return Ptr();
RectangleList<int> 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<int>& 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<int>& area, const FillType& fill, bool replaceContents)
{
(void) replaceContents; jassert (! replaceContents);
const Rectangle<int> r (clip.getIntersection (area));
if (! r.isEmpty())
{
ShaderFillOperation fillOp (*this, fill, false);
state.shaderQuadQueue.add (r, fill.colour.getPixelARGB());
}
}
void fillRect (const Rectangle<float>& 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<int>& clipArea, EdgeTable* et)
{
const Rectangle<int> 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<int> 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<OpenGLTextureFromImage> 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<int>& r) noexcept
: ClipRegionBase (s), clip (r)
{}
ClipRegion_RectangleList (GLState& s, const RectangleList<int>& 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<int>& 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<float>& area, const FillType& fill)
{
const PixelARGB colour (fill.colour.getPixelARGB());
ShaderFillOperation fillOp (*this, fill, false, false);
for (const Rectangle<int>* i = clip.begin(), * const e = clip.end(); i != e; ++i)
{
const Rectangle<float> 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<int>& 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<int> getClipBounds() const { return clip.getBounds(); }
Ptr clipToRectangle (const Rectangle<int>& r) { return clip.clipTo (r) ? this : nullptr; }
Ptr clipToRectangleList (const RectangleList<int>& r) { return clip.clipTo (r) ? this : nullptr; }
Ptr excludeClipRectangle (const Rectangle<int>& r) { clip.subtract (r); return clip.isEmpty() ? nullptr : this; }
private:
RectangleList<int> 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<OpenGLTextureFromImage> 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<int>& 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<int>& r)
{
if (clip != nullptr)
{
if (transform.isOnlyTranslated)
{
cloneClipIfMultiplyReferenced();
RectangleList<int> offsetList (r);
offsetList.offsetAll (transform.xOffset, transform.yOffset);
clip = clip->clipToRectangleList (offsetList);
}
else if (transform.isIntegerScaling)
{
cloneClipIfMultiplyReferenced();
RectangleList<int> scaledList;
for (const Rectangle<int>* 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<int>& 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<int>& r) const
{
return clip != nullptr
&& (transform.isOnlyTranslated ? clip->getClipBounds().intersects (transform.translated (r))
: getClipBounds().intersects (r));
}
Rectangle<int> getClipBounds() const
{
return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds())
: Rectangle<int>();
}
SavedState* beginTransparencyLayer (float opacity)
{
SavedState* const s = new SavedState (*this);
@ -1926,188 +1359,96 @@ public:
state->target.makeActive();
const Rectangle<int> 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<int>& 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<float>& r)
{
if (clip != nullptr)
{
if (transform.isOnlyTranslated)
{
const Rectangle<float> 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 <RenderingHelpers::CachedGlyphEdgeTable <SavedState>, 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<EdgeTable> 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<EdgeTable> 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 <float>& 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<int> (Point<int> (((ix0 + 128) >> 8),
((iy0 + 128) >> 8)),
Point<int> (((ix1 + 128) >> 8),
((iy2 + 128) >> 8))), nullptr);
}
else
{
Path p;
p.addRectangle (image.getBounds());
const Rectangle<int> clipBounds (clip->getClipBounds());
EdgeTable et (clipBounds, p, t);
clip->drawImage (image, t, alpha, clipBounds, &et);
}
}
Rectangle<int> 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 <typename IteratorType>
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 <typename IteratorType>
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 <typename IteratorType>
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 <typename IteratorType>
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<Target> 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 <SavedState>
{