Browse Source

OpenGL gradient rendering. Viewport fix.

tags/2021-05-28
jules 14 years ago
parent
commit
58580fc792
9 changed files with 237 additions and 64 deletions
  1. +34
    -35
      modules/juce_graphics/colour/juce_ColourGradient.cpp
  2. +8
    -0
      modules/juce_graphics/colour/juce_ColourGradient.h
  3. +2
    -2
      modules/juce_graphics/geometry/juce_Point.h
  4. +17
    -15
      modules/juce_gui_basics/layout/juce_Viewport.cpp
  5. +1
    -1
      modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp
  6. +142
    -0
      modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp
  7. +5
    -0
      modules/juce_opengl/opengl/juce_OpenGLHelpers.h
  8. +23
    -11
      modules/juce_opengl/opengl/juce_OpenGLTexture.cpp
  9. +5
    -0
      modules/juce_opengl/opengl/juce_OpenGLTexture.h

+ 34
- 35
modules/juce_graphics/colour/juce_ColourGradient.cpp 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
}
//==============================================================================
int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock <PixelARGB>& lookupTable) const
void ColourGradient::createLookupTable (PixelARGB* const lookupTable, const int numEntries) const noexcept
{
#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);
jassert (numEntries > 0);
jassert (colours.getReference(0).position == 0); // The first colour specified has to go at position 0
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);
PixelARGB pix1 (colours.getReference (0).colour.getPixelARGB());
int index = 0;
if (colours.size() >= 2)
for (int j = 1; j < colours.size(); ++j)
{
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;
const ColourPoint& p = colours.getReference (j);
const int numToDo = roundToInt (p.position * (numEntries - 1)) - index;
const PixelARGB pix2 (p.colour.getPixelARGB());
for (int j = 1; j < colours.size(); ++j)
for (int i = 0; i < numToDo; ++i)
{
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);
jassert (index >= 0 && index < numEntries);
lookupTable[index] = pix1;
lookupTable[index].tween (pix2, (uint32) (i << 8) / numToDo);
++index;
}
pix1 = pix2;
lookupTable[index] = pix1;
lookupTable[index].tween (pix2, (uint32) (i << 8) / numToDo);
++index;
}
while (index < numEntries)
lookupTable [index++] = pix1;
}
else
{
jassertfalse; // no colours specified!
pix1 = pix2;
}
while (index < numEntries)
lookupTable [index++] = pix1;
}
int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock <PixelARGB>& lookupTable) const
{
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);
createLookupTable (lookupTable, numEntries);
return numEntries;
}


+ 8
- 0
modules/juce_graphics/colour/juce_ColourGradient.h 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;


+ 2
- 2
modules/juce_graphics/geometry/juce_Point.h 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!


+ 17
- 15
modules/juce_gui_basics/layout/juce_Viewport.cpp 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)
{
contentComp->setTopLeftPosition (newContentCompPos); // (this will re-entrantly call updateVisibleArea again)
}
else
if (contentComp != nullptr)
{
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;
}
}
const Rectangle<int> visibleArea (visibleOrigin.getX(), visibleOrigin.getY(),
jmin (contentBounds.getWidth() - visibleOrigin.getX(), contentArea.getWidth()),
jmin (contentBounds.getHeight() - visibleOrigin.getY(), contentArea.getHeight()));
horizontalScrollBar.handleUpdateNowIfNeeded();
verticalScrollBar.handleUpdateNowIfNeeded();
if (lastVisibleArea != visibleArea)
{
lastVisibleArea = visibleArea;
visibleAreaChanged (visibleArea);
}
horizontalScrollBar.handleUpdateNowIfNeeded();
verticalScrollBar.handleUpdateNowIfNeeded();
}
//==============================================================================


+ 1
- 1
modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp 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
{


+ 142
- 0
modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp 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

+ 5
- 0
modules/juce_opengl/opengl/juce_OpenGLHelpers.h 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);
};


+ 23
- 11
modules/juce_opengl/opengl/juce_OpenGLTexture.cpp 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);
}
Image::BitmapData srcData (image, Image::BitmapData::readOnly);
void OpenGLTexture::load (const Image& image)
{
create (image.getWidth(), image.getHeight());
#if JUCE_OPENGL_ES
enum { internalFormat = GL_RGBA };
#else
enum { internalFormat = 4 };
#endif
Image::BitmapData srcData (image, Image::BitmapData::readOnly);
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)


+ 5
- 0
modules/juce_opengl/opengl/juce_OpenGLTexture.h 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);
};


Loading…
Cancel
Save