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

Improved some openGL texture creation functionality.

This commit is contained in:
jules 2013-02-28 20:45:40 +00:00
parent fe3a2e4495
commit e75663b467
8 changed files with 97 additions and 85 deletions

View file

@ -47,6 +47,7 @@ Graphics::Graphics (const Image& imageToDrawOnto)
contextToDelete (&context),
saveStatePending (false)
{
jassert (imageToDrawOnto.isValid()); // Can't draw into a null image!
}
Graphics::Graphics (LowLevelGraphicsContext* const internalContext) noexcept

View file

@ -159,6 +159,28 @@ static void clearGLError()
while (glGetError() != GL_NO_ERROR) {}
}
struct OpenGLTargetSaver
{
OpenGLTargetSaver (const OpenGLContext& c)
: context (c), oldFramebuffer (OpenGLFrameBuffer::getCurrentFrameBufferTarget())
{
glGetIntegerv (GL_VIEWPORT, oldViewport);
}
~OpenGLTargetSaver()
{
context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, oldFramebuffer);
glViewport (oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
}
private:
const OpenGLContext& context;
GLuint oldFramebuffer;
GLint oldViewport[4];
OpenGLTargetSaver& operator= (const OpenGLTargetSaver&);
};
//==============================================================================
#include "opengl/juce_OpenGLFrameBuffer.cpp"
#include "opengl/juce_OpenGLGraphicsContext.cpp"

View file

@ -244,7 +244,7 @@ public:
glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID());
const Rectangle<int> cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight());
context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight());
context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight(), false);
glBindTexture (GL_TEXTURE_2D, 0);
JUCE_CHECK_OPENGL_ERROR
}
@ -672,7 +672,8 @@ void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObjec
void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
const Rectangle<int>& anchorPosAndTextureSize,
const int contextWidth, const int contextHeight)
const int contextWidth, const int contextHeight,
bool flippedVertically)
{
if (contextWidth <= 0 || contextHeight <= 0)
return;
@ -722,12 +723,13 @@ void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
prog.addShader ("uniform sampler2D imageTexture;"
"uniform " JUCE_HIGHP " float textureBounds[4];"
"uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
"varying " JUCE_HIGHP " vec2 pixelPos;"
"void main()"
"{"
JUCE_HIGHP " vec2 texturePos = (pixelPos - vec2 (textureBounds[0], textureBounds[1]))"
"/ vec2 (textureBounds[2], textureBounds[3]);"
"gl_FragColor = texture2D (imageTexture, vec2 (texturePos.x, 1.0 - texturePos.y));"
"gl_FragColor = texture2D (imageTexture, vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y));"
"}",
GL_FRAGMENT_SHADER);
prog.link();
@ -740,19 +742,23 @@ void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
: positionAttribute (prog, "position"),
screenSize (prog, "screenSize"),
imageTexture (prog, "imageTexture"),
textureBounds (prog, "textureBounds")
textureBounds (prog, "textureBounds"),
vOffsetAndScale (prog, "vOffsetAndScale")
{}
void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds) const
void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flippedVertically) const
{
const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
textureBounds.set (m, 4);
imageTexture.set (0);
screenSize.set (targetWidth, targetHeight);
vOffsetAndScale.set (flippedVertically ? 0.0f : 1.0f,
flippedVertically ? 1.0f : -1.0f);
}
OpenGLShaderProgram::Attribute positionAttribute;
OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds;
OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
};
OpenGLShaderProgram program;
@ -767,7 +773,7 @@ void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
const OverlayShaderProgram& program = OverlayShaderProgram::select (*this);
program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat());
program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
extensions.glVertexAttribPointer (program.params.positionAttribute.attributeID, 2, GL_SHORT, GL_FALSE, 4, vertices);
extensions.glEnableVertexAttribArray (program.params.positionAttribute.attributeID);

View file

@ -219,10 +219,13 @@ public:
used for scaling of the coordinates.
@param contextHeight the height of the context or framebuffer that is being drawn into,
used for vertical flipping of the y coordinates.
@param textureOriginIsBottomLeft if true, the texture's origin is treated as being at
(0, 0). If false, it is assumed to be (0, 1)
*/
void copyTexture (const Rectangle<int>& targetClipArea,
const Rectangle<int>& anchorPosAndTextureSize,
int contextWidth, int contextHeight);
int contextWidth, int contextHeight,
bool textureOriginIsBottomLeft);
//==============================================================================

View file

@ -222,7 +222,7 @@ bool OpenGLFrameBuffer::initialise (OpenGLFrameBuffer& other)
clearGLError();
#endif
glBindTexture (GL_TEXTURE_2D, p->textureID);
pimpl->context.copyTexture (area, area, area.getWidth(), area.getHeight());
pimpl->context.copyTexture (area, area, area.getWidth(), area.getHeight(), false);
glBindTexture (GL_TEXTURE_2D, 0);
JUCE_CHECK_OPENGL_ERROR
@ -325,13 +325,14 @@ bool OpenGLFrameBuffer::readPixels (PixelARGB* target, const Rectangle<int>& are
glReadPixels (area.getX(), area.getY(), area.getWidth(), area.getHeight(),
JUCE_RGBA_FORMAT, GL_UNSIGNED_BYTE, target);
pimpl->context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, 0);
glPixelStorei (GL_PACK_ALIGNMENT, 0);
JUCE_CHECK_OPENGL_ERROR
return true;
}
bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int>& area)
{
OpenGLTargetSaver ts (pimpl->context);
if (! makeCurrentRenderingTarget())
return false;
@ -339,10 +340,10 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int>
glDisable (GL_BLEND);
JUCE_CHECK_OPENGL_ERROR
#if JUCE_OPENGL_ES && JUCE_USE_OPENGL_FIXED_FUNCTION
OpenGLTexture tex;
tex.loadARGBFlipped (data, area.getWidth(), area.getHeight());
#if JUCE_OPENGL_ES && JUCE_USE_OPENGL_FIXED_FUNCTION
const int texH = tex.getHeight();
tex.bind();
const GLint cropRect[4] = { 0, texH - area.getHeight(), area.getWidth(), area.getHeight() };
@ -353,10 +354,15 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int>
glDrawTexiOES (area.getX(), area.getY(), 1, area.getWidth(), area.getHeight());
glBindTexture (GL_TEXTURE_2D, 0);
#else
pimpl->context.copyTexture (area, area, pimpl->width, pimpl->height);
OpenGLTexture tex;
tex.loadARGB (data, area.getWidth(), area.getHeight());
glViewport (0, 0, pimpl->width, pimpl->height);
pimpl->context.copyTexture (area, Rectangle<int> (area.getX(), area.getY(),
tex.getWidth(), tex.getHeight()),
pimpl->width, pimpl->height, true);
#endif
pimpl->context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, 0);
JUCE_CHECK_OPENGL_ERROR
return true;
}

View file

@ -1364,7 +1364,7 @@ public:
clip (other.clip),
maskArea (other.clip)
{
TargetSaver ts (state.target.context);
OpenGLTargetSaver ts (state.target.context);
state.currentShader.clearShader (state.shaderQuadQueue);
state.shaderQuadQueue.flush();
state.activeTextures.setSingleTextureMode (state.shaderQuadQueue);
@ -1393,7 +1393,7 @@ public:
clip (r.getBounds()),
maskArea (clip)
{
TargetSaver ts (state.target.context);
OpenGLTargetSaver ts (state.target.context);
state.currentShader.clearShader (state.shaderQuadQueue);
state.shaderQuadQueue.flush();
state.activeTextures.clear();
@ -1429,7 +1429,7 @@ public:
if (excluded.getNumRectangles() == 1)
return excludeClipRectangle (excluded.getRectangle (0));
TargetSaver ts (state.target.context);
OpenGLTargetSaver ts (state.target.context);
makeActive();
state.blendMode.setBlendMode (state.shaderQuadQueue, true);
state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram);
@ -1445,7 +1445,7 @@ public:
if (r.contains (clip))
return Ptr();
TargetSaver ts (state.target.context);
OpenGLTargetSaver ts (state.target.context);
makeActive();
state.blendMode.setBlendMode (state.shaderQuadQueue, true);
state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram);
@ -1460,7 +1460,7 @@ public:
if (! et.isEmpty())
{
TargetSaver ts (state.target.context);
OpenGLTargetSaver ts (state.target.context);
state.currentShader.clearShader (state.shaderQuadQueue);
state.shaderQuadQueue.flush();
state.activeTextures.clear();
@ -1480,7 +1480,7 @@ public:
if (clip.isEmpty())
return Ptr();
TargetSaver ts (state.target.context);
OpenGLTargetSaver ts (state.target.context);
makeActive();
state.activeTextures.setSingleTextureMode (state.shaderQuadQueue);
@ -1501,7 +1501,7 @@ public:
Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform)
{
TargetSaver ts (state.target.context);
OpenGLTargetSaver ts (state.target.context);
makeActive();
state.activeTextures.setSingleTextureMode (state.shaderQuadQueue);
state.activeTextures.bindTexture (image.textureID);
@ -1617,28 +1617,6 @@ private:
JUCE_DECLARE_NON_COPYABLE (ShaderFillOperation)
};
struct TargetSaver
{
TargetSaver (const OpenGLContext& c)
: context (c), oldFramebuffer (OpenGLFrameBuffer::getCurrentFrameBufferTarget())
{
glGetIntegerv (GL_VIEWPORT, oldViewport);
}
~TargetSaver()
{
context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, oldFramebuffer);
glViewport (oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
}
private:
const OpenGLContext& context;
GLuint oldFramebuffer;
GLint oldViewport[4];
TargetSaver& operator= (const TargetSaver&);
};
void makeActive()
{
state.shaderQuadQueue.flush();
@ -2209,7 +2187,8 @@ public:
target.makeActive();
target.context.copyTexture (target.bounds, Rectangle<int> (texture.getWidth(),
texture.getHeight()),
target.bounds.getWidth(), target.bounds.getHeight());
target.bounds.getWidth(), target.bounds.getHeight(),
false);
glBindTexture (GL_TEXTURE_2D, 0);
#if JUCE_WINDOWS

View file

@ -38,7 +38,7 @@ bool OpenGLTexture::isValidSize (int width, int height)
return isPowerOfTwo (width) && isPowerOfTwo (height);
}
void OpenGLTexture::create (const int w, const int h, const void* pixels, GLenum type)
void OpenGLTexture::create (const int w, const int h, const void* pixels, GLenum type, bool topLeft)
{
ownerContext = OpenGLContext::getCurrentContext();
@ -46,11 +46,6 @@ void OpenGLTexture::create (const int w, const int h, const void* pixels, GLenum
// context. You'll need to create this object in one of the OpenGLContext's callbacks.
jassert (ownerContext != nullptr);
jassert (isValidSize (w, h)); // Perhaps these dimensions must be a power-of-two?
width = w;
height = h;
if (textureID == 0)
{
JUCE_CHECK_OPENGL_ERROR
@ -70,8 +65,26 @@ void OpenGLTexture::create (const int w, const int h, const void* pixels, GLenum
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
JUCE_CHECK_OPENGL_ERROR
glTexImage2D (GL_TEXTURE_2D, 0, type == GL_ALPHA ? GL_ALPHA : GL_RGBA,
w, h, 0, type, GL_UNSIGNED_BYTE, pixels);
width = nextPowerOfTwo (w);
height = nextPowerOfTwo (h);
const GLint internalformat = type == GL_ALPHA ? GL_ALPHA : GL_RGBA;
if (width != w || height != h)
{
glTexImage2D (GL_TEXTURE_2D, 0, internalformat,
width, height, 0, type, GL_UNSIGNED_BYTE, nullptr);
glTexSubImage2D (GL_TEXTURE_2D, 0, 0, topLeft ? (height - h) : 0, w, h,
type, GL_UNSIGNED_BYTE, pixels);
}
else
{
glTexImage2D (GL_TEXTURE_2D, 0, internalformat,
w, h, 0, type, GL_UNSIGNED_BYTE, pixels);
}
JUCE_CHECK_OPENGL_ERROR
}
@ -79,29 +92,20 @@ template <class PixelType>
struct Flipper
{
static void flip (HeapBlock<PixelARGB>& dataCopy, const uint8* srcData, const int lineStride,
const int w, const int h, const int textureW, const int textureH)
const int w, const int h)
{
dataCopy.malloc ((size_t) (textureW * textureH));
dataCopy.malloc ((size_t) (w * h));
for (int y = 0; y < h; ++y)
{
const PixelType* src = (const PixelType*) srcData;
PixelARGB* const dst = (PixelARGB*) (dataCopy + textureW * (textureH - 1 - y));
PixelARGB* const dst = (PixelARGB*) (dataCopy + w * (h - 1 - y));
for (int x = 0; x < w; ++x)
dst[x].set (src[x]);
if (textureW > w)
dst[w].set (PixelARGB (0));
srcData += lineStride;
}
// for textures which are larger than the area of interest, clear the pixels that lie
// just outside the actual image, so that the texture interpolation doesn't read junk.
if (textureH > h)
zeromem (dataCopy + textureW * (textureH - 1 - h),
sizeof (PixelARGB) * jmin (textureW, w + 1));
}
};
@ -109,44 +113,37 @@ void OpenGLTexture::loadImage (const Image& image)
{
const int imageW = image.getWidth();
const int imageH = image.getHeight();
const int textureW = nextPowerOfTwo (imageW);
const int textureH = nextPowerOfTwo (imageH);
HeapBlock<PixelARGB> dataCopy;
Image::BitmapData srcData (image, Image::BitmapData::readOnly);
switch (srcData.pixelFormat)
{
case Image::ARGB: Flipper<PixelARGB> ::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH, textureW, textureH); break;
case Image::RGB: Flipper<PixelRGB> ::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH, textureW, textureH); break;
case Image::SingleChannel: Flipper<PixelAlpha>::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH, textureW, textureH); break;
case Image::ARGB: Flipper<PixelARGB> ::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH); break;
case Image::RGB: Flipper<PixelRGB> ::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH); break;
case Image::SingleChannel: Flipper<PixelAlpha>::flip (dataCopy, srcData.data, srcData.lineStride, imageW, imageH); break;
default: break;
}
create (textureW, textureH, dataCopy, JUCE_RGBA_FORMAT);
create (imageW, imageH, dataCopy, JUCE_RGBA_FORMAT, true);
}
void OpenGLTexture::loadARGB (const PixelARGB* pixels, const int w, const int h)
{
jassert (isValidSize (w, h));
create (w, h, pixels, JUCE_RGBA_FORMAT);
create (w, h, pixels, JUCE_RGBA_FORMAT, false);
}
void OpenGLTexture::loadAlpha (const uint8* pixels, int w, int h)
{
jassert (isValidSize (w, h));
create (w, h, pixels, GL_ALPHA);
create (w, h, pixels, GL_ALPHA, false);
}
void OpenGLTexture::loadARGBFlipped (const PixelARGB* pixels, int w, int h)
{
const int textureW = nextPowerOfTwo (w);
const int textureH = nextPowerOfTwo (h);
HeapBlock<PixelARGB> flippedCopy;
Flipper<PixelARGB>::flip (flippedCopy, (const uint8*) pixels, 4 * w, w, h, textureW, textureH);
Flipper<PixelARGB>::flip (flippedCopy, (const uint8*) pixels, 4 * w, w, h);
loadARGB (flippedCopy, textureW, textureH);
create (w, h, flippedCopy, JUCE_RGBA_FORMAT, true);
}
void OpenGLTexture::release()

View file

@ -47,11 +47,10 @@ public:
void loadImage (const Image& image);
/** Creates a texture from a raw array of pixels.
The width and height provided must be valid - i.e. power-of-two unless
the underlying GL system allows otherwise.
If width and height are not powers-of-two, the texture will be created with a
larger size, and only the subsection (0, 0, width, height) will be initialised.
The data is sent directly to the OpenGL driver without being flipped vertically,
so the first pixel will be mapped onto texture coordinate (0, 0).
bottom-left corner of the texture
*/
void loadARGB (const PixelARGB* pixels, int width, int height);
@ -63,11 +62,10 @@ public:
void loadARGBFlipped (const PixelARGB* pixels, int width, int height);
/** Creates an alpha-channel texture from an array of alpha values.
The width and height provided must be valid - i.e. power-of-two unless
the underlying GL system allows otherwise.
If width and height are not powers-of-two, the texture will be created with a
larger size, and only the subsection (0, 0, width, height) will be initialised.
The data is sent directly to the OpenGL driver without being flipped vertically,
so the first pixel will be mapped onto texture coordinate (0, 0).
bottom-left corner of the texture
*/
void loadAlpha (const uint8* pixels, int width, int height);
@ -112,7 +110,7 @@ private:
int width, height;
OpenGLContext* ownerContext;
void create (int w, int h, const void*, GLenum type);
void create (int w, int h, const void*, GLenum, bool topLeft);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture)
};