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
|
|
@ -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