mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
OpenGL gradient rendering. Viewport fix.
This commit is contained in:
parent
b684a99d3a
commit
58580fc792
9 changed files with 242 additions and 69 deletions
|
|
@ -30,6 +30,9 @@ ColourGradient::ColourGradient() noexcept
|
|||
{
|
||||
#if JUCE_DEBUG
|
||||
point1.setX (987654.0f);
|
||||
#define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED jassert (point1.getX() != 987654.0f);
|
||||
#else
|
||||
#define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -147,52 +150,48 @@ Colour ColourGradient::getColourAtPosition (const double position) const noexcep
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
void ColourGradient::createLookupTable (PixelARGB* const lookupTable, const int numEntries) const noexcept
|
||||
{
|
||||
JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its co-ordinates?
|
||||
jassert (colours.size() >= 2);
|
||||
jassert (numEntries > 0);
|
||||
jassert (colours.getReference(0).position == 0); // The first colour specified has to go at position 0
|
||||
|
||||
PixelARGB pix1 (colours.getReference (0).colour.getPixelARGB());
|
||||
int index = 0;
|
||||
|
||||
for (int j = 1; j < colours.size(); ++j)
|
||||
{
|
||||
const ColourPoint& p = colours.getReference (j);
|
||||
const int numToDo = roundToInt (p.position * (numEntries - 1)) - index;
|
||||
const PixelARGB pix2 (p.colour.getPixelARGB());
|
||||
|
||||
for (int i = 0; i < numToDo; ++i)
|
||||
{
|
||||
jassert (index >= 0 && index < numEntries);
|
||||
|
||||
lookupTable[index] = pix1;
|
||||
lookupTable[index].tween (pix2, (uint32) (i << 8) / numToDo);
|
||||
++index;
|
||||
}
|
||||
|
||||
pix1 = pix2;
|
||||
}
|
||||
|
||||
while (index < numEntries)
|
||||
lookupTable [index++] = pix1;
|
||||
}
|
||||
|
||||
int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock <PixelARGB>& lookupTable) const
|
||||
{
|
||||
#if JUCE_DEBUG
|
||||
// trying to use the object without setting its co-ordinates? Have a careful read of
|
||||
// the comments for the constructors.
|
||||
jassert (point1.getX() != 987654.0f);
|
||||
#endif
|
||||
JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its co-ordinates?
|
||||
jassert (colours.size() >= 2);
|
||||
|
||||
const int numEntries = jlimit (1, jmax (1, (colours.size() - 1) << 8),
|
||||
3 * (int) point1.transformedBy (transform)
|
||||
.getDistanceFrom (point2.transformedBy (transform)));
|
||||
lookupTable.malloc ((size_t) numEntries);
|
||||
|
||||
if (colours.size() >= 2)
|
||||
{
|
||||
jassert (colours.getReference(0).position == 0); // the first colour specified has to go at position 0
|
||||
|
||||
PixelARGB pix1 (colours.getReference (0).colour.getPixelARGB());
|
||||
int index = 0;
|
||||
|
||||
for (int j = 1; j < colours.size(); ++j)
|
||||
{
|
||||
const ColourPoint& p = colours.getReference (j);
|
||||
const int numToDo = roundToInt (p.position * (numEntries - 1)) - index;
|
||||
const PixelARGB pix2 (p.colour.getPixelARGB());
|
||||
|
||||
for (int i = 0; i < numToDo; ++i)
|
||||
{
|
||||
jassert (index >= 0 && index < numEntries);
|
||||
|
||||
lookupTable[index] = pix1;
|
||||
lookupTable[index].tween (pix2, (uint32) (i << 8) / numToDo);
|
||||
++index;
|
||||
}
|
||||
|
||||
pix1 = pix2;
|
||||
}
|
||||
|
||||
while (index < numEntries)
|
||||
lookupTable [index++] = pix1;
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse; // no colours specified!
|
||||
}
|
||||
|
||||
createLookupTable (lookupTable, numEntries);
|
||||
return numEntries;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,9 +128,17 @@ public:
|
|||
/** Creates a set of interpolated premultiplied ARGB values.
|
||||
This will resize the HeapBlock, fill it with the colours, and will return the number of
|
||||
colours that it added.
|
||||
When calling this, the ColourGradient must have at least 2 colour stops specified.
|
||||
*/
|
||||
int createLookupTable (const AffineTransform& transform, HeapBlock <PixelARGB>& resultLookupTable) const;
|
||||
|
||||
/** Creates a set of interpolated premultiplied ARGB values.
|
||||
This will fill an array of a user-specified size with the gradient, interpolating to fit.
|
||||
The numEntries argument specifies the size of the array, and this size must be greater than zero.
|
||||
When calling this, the ColourGradient must have at least 2 colour stops specified.
|
||||
*/
|
||||
void createLookupTable (PixelARGB* resultLookupTable, int numEntries) const noexcept;
|
||||
|
||||
/** Returns true if all colours are opaque. */
|
||||
bool isOpaque() const noexcept;
|
||||
|
||||
|
|
|
|||
|
|
@ -137,14 +137,14 @@ public:
|
|||
@param angle the angle of the point, in radians clockwise from the 12 o'clock position.
|
||||
*/
|
||||
Point getPointOnCircumference (const float radius, const float angle) const noexcept { return Point<float> (x + radius * std::sin (angle),
|
||||
y - radius * std::cos (angle)); }
|
||||
y - radius * std::cos (angle)); }
|
||||
/** Taking this point to be the centre of an ellipse, this returns a point on its circumference.
|
||||
@param radiusX the horizontal radius of the circle.
|
||||
@param radiusY the vertical radius of the circle.
|
||||
@param angle the angle of the point, in radians clockwise from the 12 o'clock position.
|
||||
*/
|
||||
Point getPointOnCircumference (const float radiusX, const float radiusY, const float angle) const noexcept { return Point<float> (x + radiusX * std::sin (angle),
|
||||
y - radiusY * std::cos (angle)); }
|
||||
y - radiusY * std::cos (angle)); }
|
||||
|
||||
/** Uses a transform to change the point's co-ordinates.
|
||||
This will only compile if ValueType = float!
|
||||
|
|
|
|||
|
|
@ -253,27 +253,29 @@ void Viewport::updateVisibleArea()
|
|||
horizontalScrollBar.setVisible (hBarVisible);
|
||||
verticalScrollBar.setVisible (vBarVisible);
|
||||
|
||||
const Point<int> newContentCompPos (viewportPosToCompPos (visibleOrigin));
|
||||
|
||||
if (contentComp != nullptr && contentComp->getBounds().getPosition() != newContentCompPos)
|
||||
if (contentComp != nullptr)
|
||||
{
|
||||
contentComp->setTopLeftPosition (newContentCompPos); // (this will re-entrantly call updateVisibleArea again)
|
||||
}
|
||||
else
|
||||
{
|
||||
const Rectangle<int> visibleArea (visibleOrigin.getX(), visibleOrigin.getY(),
|
||||
jmin (contentBounds.getWidth() - visibleOrigin.getX(), contentArea.getWidth()),
|
||||
jmin (contentBounds.getHeight() - visibleOrigin.getY(), contentArea.getHeight()));
|
||||
const Point<int> newContentCompPos (viewportPosToCompPos (visibleOrigin));
|
||||
|
||||
if (lastVisibleArea != visibleArea)
|
||||
if (contentComp->getBounds().getPosition() != newContentCompPos)
|
||||
{
|
||||
lastVisibleArea = visibleArea;
|
||||
visibleAreaChanged (visibleArea);
|
||||
contentComp->setTopLeftPosition (newContentCompPos); // (this will re-entrantly call updateVisibleArea again)
|
||||
return;
|
||||
}
|
||||
|
||||
horizontalScrollBar.handleUpdateNowIfNeeded();
|
||||
verticalScrollBar.handleUpdateNowIfNeeded();
|
||||
}
|
||||
|
||||
const Rectangle<int> visibleArea (visibleOrigin.getX(), visibleOrigin.getY(),
|
||||
jmin (contentBounds.getWidth() - visibleOrigin.getX(), contentArea.getWidth()),
|
||||
jmin (contentBounds.getHeight() - visibleOrigin.getY(), contentArea.getHeight()));
|
||||
|
||||
if (lastVisibleArea != visibleArea)
|
||||
{
|
||||
lastVisibleArea = visibleArea;
|
||||
visibleAreaChanged (visibleArea);
|
||||
}
|
||||
|
||||
horizontalScrollBar.handleUpdateNowIfNeeded();
|
||||
verticalScrollBar.handleUpdateNowIfNeeded();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ public:
|
|||
private:
|
||||
// Some GL implementations can't take very large triangle lists, so store
|
||||
// the list as a series of blocks containing this max number of triangles.
|
||||
enum { trianglesPerBlock = 2048 };
|
||||
enum { trianglesPerBlock = 256 };
|
||||
|
||||
struct TriangleBlock
|
||||
{
|
||||
|
|
|
|||
|
|
@ -121,4 +121,146 @@ void OpenGLHelpers::drawQuad3D (float x1, float y1, float z1,
|
|||
glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
namespace OpenGLGradientHelpers
|
||||
{
|
||||
void drawTriangles (GLenum mode, const GLfloat* vertices, const GLfloat* textureCoords, const int numElements)
|
||||
{
|
||||
glEnableClientState (GL_VERTEX_ARRAY);
|
||||
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState (GL_COLOR_ARRAY);
|
||||
glDisableClientState (GL_NORMAL_ARRAY);
|
||||
|
||||
glVertexPointer (2, GL_FLOAT, 0, vertices);
|
||||
glTexCoordPointer (2, GL_FLOAT, 0, textureCoords);
|
||||
|
||||
glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glDrawArrays (mode, 0, numElements);
|
||||
}
|
||||
|
||||
void fillWithLinearGradient (const Rectangle<int>& rect,
|
||||
const ColourGradient& grad,
|
||||
const AffineTransform& transform,
|
||||
const int textureSize)
|
||||
{
|
||||
const Point<float> p1 (grad.point1.transformedBy (transform));
|
||||
const Point<float> p2 (grad.point2.transformedBy (transform));
|
||||
const Point<float> p3 (Point<float> (grad.point1.getX() - (grad.point2.getY() - grad.point1.getY()) / textureSize,
|
||||
grad.point1.getY() + (grad.point2.getX() - grad.point1.getX()) / textureSize).transformedBy (transform));
|
||||
|
||||
const AffineTransform textureTransform (AffineTransform::fromTargetPoints (p1.getX(), p1.getY(), 0.0f, 0.0f,
|
||||
p2.getX(), p2.getY(), 1.0f, 0.0f,
|
||||
p3.getX(), p3.getY(), 0.0f, 1.0f));
|
||||
|
||||
const float l = (float) rect.getX();
|
||||
const float r = (float) rect.getRight();
|
||||
const float t = (float) rect.getY();
|
||||
const float b = (float) rect.getBottom();
|
||||
|
||||
const GLfloat vertices[] = { l, t, r, t, l, b, r, b };
|
||||
GLfloat textureCoords[] = { l, t, r, t, l, b, r, b };
|
||||
|
||||
textureTransform.transformPoints (textureCoords[0], textureCoords[1], textureCoords[2], textureCoords[3]);
|
||||
textureTransform.transformPoints (textureCoords[4], textureCoords[5], textureCoords[6], textureCoords[7]);
|
||||
|
||||
drawTriangles (GL_TRIANGLE_STRIP, vertices, textureCoords, 4);
|
||||
}
|
||||
|
||||
void fillWithRadialGradient (const Rectangle<int>& rect,
|
||||
const ColourGradient& grad,
|
||||
const AffineTransform& transform)
|
||||
{
|
||||
const Point<float> centre (grad.point1.transformedBy (transform));
|
||||
|
||||
const float screenRadius = centre.getDistanceFrom (rect.getCentre().toFloat())
|
||||
+ Point<int> (rect.getWidth() / 2,
|
||||
rect.getHeight() / 2).getDistanceFromOrigin()
|
||||
+ 8.0f;
|
||||
|
||||
const AffineTransform inverse (transform.inverted());
|
||||
const float renderingRadius = jmax (Point<float> (screenRadius, 0.0f).transformedBy (inverse).getDistanceFromOrigin(),
|
||||
Point<float> (0.0f, screenRadius).transformedBy (inverse).getDistanceFromOrigin());
|
||||
|
||||
const int numDivisions = 80;
|
||||
GLfloat vertices [6 + numDivisions * 4];
|
||||
GLfloat textureCoords [6 + numDivisions * 4];
|
||||
|
||||
{
|
||||
const float originalRadius = grad.point1.getDistanceFrom (grad.point2);
|
||||
const float texturePos = renderingRadius / originalRadius;
|
||||
|
||||
GLfloat* t = textureCoords;
|
||||
*t++ = 0.0f;
|
||||
*t++ = 0.0f;
|
||||
|
||||
for (int i = numDivisions + 1; --i >= 0;)
|
||||
{
|
||||
*t++ = texturePos;
|
||||
*t++ = 0.0f;
|
||||
*t++ = texturePos;
|
||||
*t++ = 1.0f;
|
||||
}
|
||||
|
||||
jassert (t == textureCoords + numElementsInArray (vertices));
|
||||
}
|
||||
|
||||
{
|
||||
GLfloat* v = vertices;
|
||||
|
||||
*v++ = centre.getX();
|
||||
*v++ = centre.getY();
|
||||
|
||||
const Point<float> first (grad.point1.translated (renderingRadius, -renderingRadius).transformedBy (transform));
|
||||
Point<float> last (first);
|
||||
|
||||
for (int i = 0; i < numDivisions; ++i)
|
||||
{
|
||||
const float angle = (i + 1) * (float_Pi * 4.0f / numDivisions);
|
||||
const Point<float> next (grad.point1.translated (std::sin (angle) * renderingRadius,
|
||||
-std::cos (angle) * renderingRadius)
|
||||
.transformedBy (transform));
|
||||
*v++ = last.getX();
|
||||
*v++ = last.getY();
|
||||
*v++ = next.getX();
|
||||
*v++ = next.getY();
|
||||
last = next;
|
||||
}
|
||||
|
||||
*v++ = last.getX();
|
||||
*v++ = last.getY();
|
||||
*v++ = first.getX();
|
||||
*v++ = first.getY();
|
||||
|
||||
jassert (v == vertices + numElementsInArray (vertices));
|
||||
}
|
||||
|
||||
glEnable (GL_SCISSOR_TEST);
|
||||
glScissor (rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
|
||||
drawTriangles (GL_TRIANGLE_FAN, vertices, textureCoords, numDivisions + 3);
|
||||
glDisable (GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLHelpers::fillRectWithColourGradient (const Rectangle<int>& rect,
|
||||
const ColourGradient& gradient,
|
||||
const AffineTransform& transform)
|
||||
{
|
||||
const int textureSize = 256;
|
||||
OpenGLTexture texture;
|
||||
|
||||
HeapBlock<PixelARGB> lookup (textureSize);
|
||||
gradient.createLookupTable (lookup, textureSize);
|
||||
texture.load (lookup, textureSize, 1);
|
||||
texture.bind();
|
||||
|
||||
if (gradient.isOpaque())
|
||||
glDisable (GL_BLEND);
|
||||
else
|
||||
glEnable (GL_BLEND);
|
||||
|
||||
if (gradient.isRadial)
|
||||
OpenGLGradientHelpers::fillWithRadialGradient (rect, gradient, transform);
|
||||
else
|
||||
OpenGLGradientHelpers::fillWithLinearGradient (rect, gradient, transform, textureSize);
|
||||
}
|
||||
|
||||
END_JUCE_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -60,6 +60,11 @@ public:
|
|||
float x3, float y3, float z3,
|
||||
float x4, float y4, float z4,
|
||||
const Colour& colour);
|
||||
|
||||
/** Fills a rectangle with the specified gradient. */
|
||||
static void fillRectWithColourGradient (const Rectangle<int>& rect,
|
||||
const ColourGradient& gradient,
|
||||
const AffineTransform& transform);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@
|
|||
|
||||
BEGIN_JUCE_NAMESPACE
|
||||
|
||||
#if JUCE_OPENGL_ES
|
||||
enum { internalGLTextureFormat = GL_RGBA };
|
||||
#else
|
||||
enum { internalGLTextureFormat = 4 };
|
||||
#endif
|
||||
|
||||
|
||||
OpenGLTexture::OpenGLTexture()
|
||||
: textureID (0), width (0), height (0)
|
||||
|
|
@ -36,12 +42,12 @@ OpenGLTexture::~OpenGLTexture()
|
|||
release();
|
||||
}
|
||||
|
||||
void OpenGLTexture::load (const Image& image)
|
||||
void OpenGLTexture::create (const int w, const int h)
|
||||
{
|
||||
release();
|
||||
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
width = w;
|
||||
height = h;
|
||||
|
||||
jassert (BitArray (width).countNumberOfSetBits() == 1); // these dimensions must be a power-of-two
|
||||
jassert (BitArray (height).countNumberOfSetBits() == 1);
|
||||
|
|
@ -57,21 +63,27 @@ void OpenGLTexture::load (const Image& image)
|
|||
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
|
||||
}
|
||||
|
||||
void OpenGLTexture::load (const Image& image)
|
||||
{
|
||||
create (image.getWidth(), image.getHeight());
|
||||
|
||||
Image::BitmapData srcData (image, Image::BitmapData::readOnly);
|
||||
|
||||
#if JUCE_OPENGL_ES
|
||||
enum { internalFormat = GL_RGBA };
|
||||
#else
|
||||
enum { internalFormat = 4 };
|
||||
#endif
|
||||
|
||||
glTexImage2D (GL_TEXTURE_2D, 0, internalFormat,
|
||||
width, height, 0,
|
||||
glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, width, height, 0,
|
||||
image.getFormat() == Image::RGB ? GL_RGB : GL_BGRA_EXT,
|
||||
GL_UNSIGNED_BYTE, srcData.data);
|
||||
}
|
||||
|
||||
void OpenGLTexture::load (const PixelARGB* const pixels, const int w, const int h)
|
||||
{
|
||||
create (w, h);
|
||||
|
||||
glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, w, h, 0,
|
||||
GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
|
||||
}
|
||||
|
||||
void OpenGLTexture::release()
|
||||
{
|
||||
if (textureID != 0)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ public:
|
|||
*/
|
||||
void load (const Image& image);
|
||||
|
||||
/** Creates a texture from a raw array of pixels. */
|
||||
void load (const PixelARGB* pixels, int width, int height);
|
||||
|
||||
/** Frees the texture, if there is one. */
|
||||
void release();
|
||||
|
||||
|
|
@ -67,6 +70,8 @@ private:
|
|||
unsigned int textureID;
|
||||
int width, height;
|
||||
|
||||
void create (int w, int h);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue