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

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

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

View file

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