1
0
Fork 0
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:
jules 2011-10-08 14:09:00 +01:00
parent b684a99d3a
commit 58580fc792
9 changed files with 242 additions and 69 deletions

View file

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

View file

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

View file

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

View file

@ -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();
}
//==============================================================================

View file

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

View file

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

View file

@ -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);
};

View file

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

View file

@ -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);
};