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:
parent
08c9d5d22f
commit
67916bc9d0
3 changed files with 362 additions and 993 deletions
|
|
@ -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&);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue