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

OpenGL context now supports high DPI displays. Added operator* to Rectangle class.

This commit is contained in:
jules 2012-11-15 17:55:57 +00:00
parent 667a18712f
commit f6ce67ed4a
13 changed files with 166 additions and 98 deletions

View file

@ -109,14 +109,8 @@ public:
{
OpenGLHelpers::clear (Colours::darkgrey.withAlpha (1.0f));
{
MessageManagerLock mm (Thread::getCurrentThread());
if (! mm.lockWasGained())
return;
updateTextureImage(); // this will update our dynamically-changing texture image.
drawBackground2DStuff(); // draws some 2D content to demonstrate the OpenGLGraphicsContext class
}
updateTextureImage(); // this will update our dynamically-changing texture image.
drawBackground2DStuff(); // draws some 2D content to demonstrate the OpenGLGraphicsContext class
// Having used the juce 2D renderer, it will have messed-up a whole load of GL state, so
// we'll put back any important settings before doing our normal GL 3D drawing..
@ -127,8 +121,8 @@ public:
glEnable (GL_TEXTURE_2D);
#if JUCE_USE_OPENGL_FIXED_FUNCTION
OpenGLHelpers::prepareFor2D (getWidth(), getHeight());
OpenGLHelpers::setPerspective (45.0, getWidth() / (double) getHeight(), 0.1, 100.0);
OpenGLHelpers::prepareFor2D (getContextWidth(), getContextHeight());
OpenGLHelpers::setPerspective (45.0, getContextWidth() / (double) getContextHeight(), 0.1, 100.0);
glTranslatef (0.0f, 0.0f, -5.0f);
draggableOrientation.applyToOpenGLMatrix();
@ -173,11 +167,14 @@ public:
void drawBackground2DStuff()
{
// Create an OpenGLGraphicsContext that will draw into this GL window..
ScopedPointer<LowLevelGraphicsContext> glRenderer (createOpenGLGraphicsContext (openGLContext));
ScopedPointer<LowLevelGraphicsContext> glRenderer (createOpenGLGraphicsContext (openGLContext,
getContextWidth(),
getContextHeight()));
if (glRenderer != nullptr)
{
Graphics g (glRenderer);
g.addTransform (AffineTransform::scale ((float) getScale()));
// This stuff just creates a spinning star shape and fills it..
Path p;
@ -194,6 +191,10 @@ public:
}
}
double getScale() const { return Desktop::getInstance().getDisplays().getDisplayContaining (getScreenBounds().getCentre()).scale; }
int getContextWidth() const { return roundToInt (getScale() * getWidth()); }
int getContextHeight() const { return roundToInt (getScale() * getHeight()); }
void timerCallback()
{
rotation += (float) speedSlider.getValue();

View file

@ -149,8 +149,12 @@ AffineTransform AffineTransform::scaled (const float factorX, const float factor
AffineTransform AffineTransform::scale (const float factorX, const float factorY) noexcept
{
return AffineTransform (factorX, 0, 0,
0, factorY, 0);
return AffineTransform (factorX, 0, 0, 0, factorY, 0);
}
AffineTransform AffineTransform::scale (const float factor) noexcept
{
return AffineTransform (factor, 0, 0, 0, factor, 0);
}
AffineTransform AffineTransform::scaled (const float factorX, const float factorY,

View file

@ -173,6 +173,9 @@ public:
static AffineTransform scale (float factorX,
float factorY) noexcept;
/** Returns a new transform which is a re-scale about the origin. */
static AffineTransform scale (float factor) noexcept;
/** Returns a new transform which is a re-scale centred around the point provided. */
static AffineTransform scale (float factorX, float factorY,
float pivotX, float pivotY) noexcept;

View file

@ -292,6 +292,25 @@ public:
return *this;
}
/** Scales this rectangle by the given amount, centred around the origin. */
template <typename FloatType>
Rectangle operator* (FloatType scaleFactor) const noexcept
{
Rectangle r (*this);
r *= scaleFactor;
return r;
}
/** Scales this rectangle by the given amount, centred around the origin. */
template <typename FloatType>
Rectangle operator*= (FloatType scaleFactor) noexcept
{
pos *= scaleFactor;
w *= scaleFactor;
h *= scaleFactor;
return *this;
}
/** Expands the rectangle by a given amount.
Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2).

View file

@ -186,7 +186,7 @@ public:
{
JNIEnv* env = getEnv();
jobject matrix = GraphicsHelpers::createMatrix (env, AffineTransform::scale (unitsToHeightScaleFactor, unitsToHeightScaleFactor).followedBy (t));
jobject matrix = GraphicsHelpers::createMatrix (env, AffineTransform::scale (unitsToHeightScaleFactor).followedBy (t));
jintArray maskData = (jintArray) android.activity.callObjectMethod (JuceAppActivity.renderGlyph, (jchar) glyphNumber, paint.get(), matrix, rect.get());
env->DeleteLocalRef (matrix);

View file

@ -178,7 +178,7 @@ public:
const float pathAscent = (1024.0f * dwFontMetrics.ascent) / designUnitsPerEm;
const float pathDescent = (1024.0f * dwFontMetrics.descent) / designUnitsPerEm;
const float pathScale = 1.0f / (std::abs (pathAscent) + std::abs (pathDescent));
pathTransform = AffineTransform::scale (pathScale, pathScale);
pathTransform = AffineTransform::scale (pathScale);
}
float getAscent() const { return ascent; }

View file

@ -1911,12 +1911,12 @@ void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel)
(int) (scale * getWidth()), (int) (scale * getHeight()), ! flags.opaqueFlag);
{
Graphics g2 (effectImage);
g2.addTransform (AffineTransform::scale (scale, scale));
g2.addTransform (AffineTransform::scale (scale));
paintComponentAndChildren (g2);
}
g.saveState();
g.addTransform (AffineTransform::scale (1.0f / scale, 1.0f / scale));
g.addTransform (AffineTransform::scale (1.0f / scale));
effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : getAlpha());
g.restoreState();
}

View file

@ -1097,7 +1097,7 @@ private:
else if (t.startsWithIgnoreCase ("scale"))
{
if (tokens.size() == 1)
trans = AffineTransform::scale (numbers[0], numbers[0]);
trans = AffineTransform::scale (numbers[0]);
else
trans = AffineTransform::scale (numbers[0], numbers[1]);
}

View file

@ -43,6 +43,12 @@ struct ThreadSafeNSOpenGLViewClass : public ObjCClass <NSOpenGLView>
static void init (id self)
{
object_setInstanceVariable (self, "lock", new CriticalSection());
#if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
if ([self respondsToSelector: @selector (setWantsBestResolutionOpenGLSurface:)])
[self setWantsBestResolutionOpenGLSurface: YES];
#endif
setNeedsUpdate (self, YES);
}

View file

@ -27,12 +27,11 @@ class OpenGLContext::CachedImage : public CachedComponentImage,
public Thread
{
public:
CachedImage (OpenGLContext& context_,
Component& component_,
const OpenGLPixelFormat& pixelFormat,
void* contextToShareWith)
CachedImage (OpenGLContext& c, Component& comp,
const OpenGLPixelFormat& pixelFormat, void* contextToShareWith)
: Thread ("OpenGL Rendering"),
context (context_), component (component_),
context (c), component (comp),
scale (1.0),
#if JUCE_OPENGL_ES
shadersAvailable (true),
#else
@ -102,14 +101,14 @@ public:
}
//==============================================================================
bool ensureFrameBufferSize (int width, int height)
bool ensureFrameBufferSize()
{
const int fbW = cachedImageFrameBuffer.getWidth();
const int fbH = cachedImageFrameBuffer.getHeight();
if (fbW != width || fbH != height || ! cachedImageFrameBuffer.isValid())
if (fbW != viewportArea.getWidth() || fbH != viewportArea.getHeight() || ! cachedImageFrameBuffer.isValid())
{
if (! cachedImageFrameBuffer.initialise (context, width, height))
if (! cachedImageFrameBuffer.initialise (context, viewportArea.getWidth(), viewportArea.getHeight()))
return false;
validArea.clear();
@ -119,18 +118,19 @@ public:
return true;
}
void clearRegionInFrameBuffer (const RectangleList& list)
void clearRegionInFrameBuffer (const RectangleList& list, const double scale)
{
glClearColor (0, 0, 0, 0);
glEnable (GL_SCISSOR_TEST);
const GLuint previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget();
cachedImageFrameBuffer.makeCurrentRenderingTarget();
const int imageH = cachedImageFrameBuffer.getHeight();
for (RectangleList::Iterator i (list); i.next();)
for (const Rectangle<int>* i = list.begin(), * const e = list.end(); i != e; ++i)
{
const Rectangle<int>& r = *i.getRectangle();
glScissor (r.getX(), component.getHeight() - r.getBottom(), r.getWidth(), r.getHeight());
const Rectangle<int> r (*i * scale);
glScissor (r.getX(), imageH - r.getBottom(), r.getWidth(), r.getHeight());
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
@ -141,13 +141,21 @@ public:
bool renderFrame()
{
ScopedPointer<MessageManagerLock> mmLock;
if (context.renderComponents && needsUpdate)
{
mmLock = new MessageManagerLock (this); // need to acquire this before locking the context.
if (! mmLock->lockWasGained())
return false;
}
if (! context.makeActive())
return false;
NativeContext::Locker locker (*nativeContext);
JUCE_CHECK_OPENGL_ERROR
glViewport (0, 0, component.getWidth(), component.getHeight());
glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
if (context.renderer != nullptr)
{
@ -156,51 +164,76 @@ public:
}
if (context.renderComponents)
paintComponent();
{
if (needsUpdate)
{
needsUpdate = false;
paintComponent();
mmLock = nullptr;
}
glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
drawComponentBuffer();
}
context.swapBuffers();
return true;
}
void updateViewportSize (bool canTriggerUpdate)
{
const double newScale = Desktop::getInstance().getDisplays()
.getDisplayContaining (component.getScreenBounds().getCentre()).scale;
Rectangle<int> newArea (roundToInt (component.getWidth() * newScale),
roundToInt (component.getHeight() * newScale));
if (scale != newScale || viewportArea != newArea)
{
scale = newScale;
viewportArea = newArea;
if (canTriggerUpdate)
invalidateAll();
}
}
void paintComponent()
{
if (needsUpdate)
// you mustn't set your own cached image object when attaching a GL context!
jassert (get (component) == this);
updateViewportSize (false);
if (! ensureFrameBufferSize())
return;
RectangleList invalid (viewportArea);
invalid.subtract (validArea);
validArea = viewportArea;
if (! invalid.isEmpty())
{
MessageManagerLock mm (this);
if (! mm.lockWasGained())
return;
clearRegionInFrameBuffer (invalid, scale);
needsUpdate = false;
// you mustn't set your own cached image object when attaching a GL context!
jassert (get (component) == this);
const Rectangle<int> bounds (component.getLocalBounds());
if (! ensureFrameBufferSize (bounds.getWidth(), bounds.getHeight()))
return;
RectangleList invalid (bounds);
invalid.subtract (validArea);
validArea = bounds;
if (! invalid.isEmpty())
{
clearRegionInFrameBuffer (invalid);
ScopedPointer<LowLevelGraphicsContext> g (createOpenGLGraphicsContext (context, cachedImageFrameBuffer));
g->addTransform (AffineTransform::scale ((float) scale));
g->clipToRectangleList (invalid);
{
ScopedPointer<LowLevelGraphicsContext> g (createOpenGLGraphicsContext (context, cachedImageFrameBuffer));
g->clipToRectangleList (invalid);
paintOwner (*g);
JUCE_CHECK_OPENGL_ERROR
}
if (! context.isActive())
context.makeActive();
paintOwner (*g);
JUCE_CHECK_OPENGL_ERROR
}
JUCE_CHECK_OPENGL_ERROR
if (! context.isActive())
context.makeActive();
}
JUCE_CHECK_OPENGL_ERROR
}
void drawComponentBuffer()
{
#if ! JUCE_ANDROID
glEnable (GL_TEXTURE_2D);
clearGLError();
@ -209,7 +242,7 @@ public:
glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID());
const Rectangle<int> cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight());
context.copyTexture (cacheBounds, cacheBounds, context.getWidth(), context.getHeight());
context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight());
glBindTexture (GL_TEXTURE_2D, 0);
JUCE_CHECK_OPENGL_ERROR
}
@ -316,6 +349,8 @@ public:
OpenGLFrameBuffer cachedImageFrameBuffer;
RectangleList validArea;
Rectangle<int> viewportArea;
double scale;
StringArray associatedObjectNames;
ReferenceCountedArray<ReferenceCountedObject> associatedObjects;
@ -333,11 +368,10 @@ void OpenGLContext::NativeContext::contextCreatedCallback()
{
isInsideGLCallback = true;
CachedImage* const c = CachedImage::get (component);
jassert (c != nullptr);
if (c != nullptr)
if (CachedImage* const c = CachedImage::get (component))
c->initialiseOnThread();
else
jassertfalse;
isInsideGLCallback = false;
}
@ -357,8 +391,8 @@ void OpenGLContext::NativeContext::renderCallback()
class OpenGLContext::Attachment : public ComponentMovementWatcher
{
public:
Attachment (OpenGLContext& context_, Component& comp)
: ComponentMovementWatcher (&comp), context (context_)
Attachment (OpenGLContext& c, Component& comp)
: ComponentMovementWatcher (&comp), context (c)
{
if (canBeAttached (comp))
attach();
@ -376,12 +410,12 @@ public:
if (isAttached (comp) != canBeAttached (comp))
componentVisibilityChanged();
context.width = comp.getWidth();
context.height = comp.getHeight();
if (comp.getWidth() > 0 && comp.getHeight() > 0
&& context.nativeContext != nullptr)
{
if (CachedImage* const c = CachedImage::get (comp))
c->updateViewportSize (true);
context.nativeContext->updateWindowPosition (comp.getTopLevelComponent()
->getLocalArea (&comp, comp.getLocalBounds()));
}
@ -458,7 +492,7 @@ private:
//==============================================================================
OpenGLContext::OpenGLContext()
: nativeContext (nullptr), renderer (nullptr), contextToShareWith (nullptr),
width (0), height (0), renderComponents (true)
renderComponents (true)
{
}
@ -510,10 +544,6 @@ void OpenGLContext::attachTo (Component& component)
if (getTargetComponent() != &component)
{
detach();
width = component.getWidth();
height = component.getHeight();
attachment = new Attachment (*this, component);
}
}
@ -522,7 +552,6 @@ void OpenGLContext::detach()
{
attachment = nullptr;
nativeContext = nullptr;
width = height = 0;
}
bool OpenGLContext::isAttached() const noexcept

View file

@ -129,14 +129,7 @@ public:
/** Asynchronously causes a repaint to be made. */
void triggerRepaint();
//==============================================================================
/** Returns the width of this context */
inline int getWidth() const noexcept { return width; }
/** Returns the height of this context */
inline int getHeight() const noexcept { return height; }
/** If this context is backed by a frame buffer, this returns its ID number,
or 0 if the context does not use a framebuffer.
*/
@ -245,7 +238,6 @@ private:
ScopedPointer<Attachment> attachment;
OpenGLPixelFormat pixelFormat;
void* contextToShareWith;
int width, height;
bool renderComponents;
CachedImage* getCachedImage() const noexcept;

View file

@ -991,15 +991,15 @@ struct StateHelpers
void add (const RectangleList& list, const PixelARGB& colour) noexcept
{
for (RectangleList::Iterator i (list); i.next();)
add (*i.getRectangle(), colour);
for (const Rectangle<int>* i = list.begin(), * const e = list.end(); i != e; ++i)
add (*i, colour);
}
void add (const RectangleList& list, const Rectangle<int>& clip, const PixelARGB& colour) noexcept
{
for (RectangleList::Iterator i (list); i.next();)
for (const Rectangle<int>* i = list.begin(), * const e = list.end(); i != e; ++i)
{
const Rectangle<int> r (i.getRectangle()->getIntersection (clip));
const Rectangle<int> r (i->getIntersection (clip));
if (! r.isEmpty())
add (r, colour);
@ -1714,9 +1714,9 @@ public:
const PixelARGB colour (fill.colour.getPixelARGB());
ShaderFillOperation fillOp (*this, fill, false, false);
for (RectangleList::Iterator i (clip); i.next();)
for (const Rectangle<int>* i = clip.begin(), * const e = clip.end(); i != e; ++i)
{
const Rectangle<float> r (i.getRectangle()->toFloat().getIntersection (area));
const Rectangle<float> r (i->toFloat().getIntersection (area));
if (! r.isEmpty())
state.shaderQuadQueue.add (r, colour);
}
@ -1832,6 +1832,11 @@ public:
cloneClipIfMultiplyReferenced();
clip = clip->clipToRectangle (transform.translated (r));
}
else if (transform.isIntegerScaling)
{
cloneClipIfMultiplyReferenced();
clip = clip->clipToRectangle (transform.transformed (r));
}
else
{
Path p;
@ -1854,6 +1859,16 @@ public:
offsetList.offsetAll (transform.xOffset, transform.yOffset);
clip = clip->clipToRectangleList (offsetList);
}
else if (transform.isIntegerScaling)
{
cloneClipIfMultiplyReferenced();
RectangleList scaledList;
for (const Rectangle<int>* i = r.begin(), * const e = r.end(); i != e; ++i)
scaledList.add (transform.transformed (*i));
clip = clip->clipToRectangleList (scaledList);
}
else
{
clipToPath (r.toPath(), AffineTransform::identity);
@ -2234,20 +2249,18 @@ LowLevelGraphicsContext* createOpenGLContext (const Target& target)
}
//==============================================================================
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context)
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, int width, int height)
{
return createOpenGLGraphicsContext (context, context.getFrameBufferID(),
context.getWidth(), context.getHeight());
return createOpenGLGraphicsContext (context, context.getFrameBufferID(), width, height);
}
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, OpenGLFrameBuffer& target)
{
using namespace OpenGLRendering;
return createOpenGLContext (Target (context, target, Point<int>()));
return OpenGLRendering::createOpenGLContext (OpenGLRendering::Target (context, target, Point<int>()));
}
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& context, unsigned int frameBufferID, int width, int height)
{
using namespace OpenGLRendering;
return createOpenGLContext (Target (context, frameBufferID, width, height));
return OpenGLRendering::createOpenGLContext (OpenGLRendering::Target (context, frameBufferID, width, height));
}

View file

@ -30,7 +30,8 @@
/** Creates a graphics context object that will render into the given OpenGL target.
The caller is responsible for deleting this object when no longer needed.
*/
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& target);
LowLevelGraphicsContext* createOpenGLGraphicsContext (OpenGLContext& target,
int width, int height);
/** Creates a graphics context object that will render into the given OpenGL target.
The caller is responsible for deleting this object when no longer needed.