| @@ -47,6 +47,7 @@ Graphics::Graphics (const Image& imageToDrawOnto) | |||||
| contextToDelete (&context), | contextToDelete (&context), | ||||
| saveStatePending (false) | saveStatePending (false) | ||||
| { | { | ||||
| jassert (imageToDrawOnto.isValid()); // Can't draw into a null image! | |||||
| } | } | ||||
| Graphics::Graphics (LowLevelGraphicsContext* const internalContext) noexcept | Graphics::Graphics (LowLevelGraphicsContext* const internalContext) noexcept | ||||
| @@ -159,6 +159,28 @@ static void clearGLError() | |||||
| while (glGetError() != GL_NO_ERROR) {} | 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_OpenGLFrameBuffer.cpp" | ||||
| #include "opengl/juce_OpenGLGraphicsContext.cpp" | #include "opengl/juce_OpenGLGraphicsContext.cpp" | ||||
| @@ -244,7 +244,7 @@ public: | |||||
| glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID()); | glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID()); | ||||
| const Rectangle<int> cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight()); | 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); | glBindTexture (GL_TEXTURE_2D, 0); | ||||
| JUCE_CHECK_OPENGL_ERROR | JUCE_CHECK_OPENGL_ERROR | ||||
| } | } | ||||
| @@ -672,7 +672,8 @@ void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObjec | |||||
| void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea, | void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea, | ||||
| const Rectangle<int>& anchorPosAndTextureSize, | const Rectangle<int>& anchorPosAndTextureSize, | ||||
| const int contextWidth, const int contextHeight) | |||||
| const int contextWidth, const int contextHeight, | |||||
| bool flippedVertically) | |||||
| { | { | ||||
| if (contextWidth <= 0 || contextHeight <= 0) | if (contextWidth <= 0 || contextHeight <= 0) | ||||
| return; | return; | ||||
| @@ -722,12 +723,13 @@ void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea, | |||||
| prog.addShader ("uniform sampler2D imageTexture;" | prog.addShader ("uniform sampler2D imageTexture;" | ||||
| "uniform " JUCE_HIGHP " float textureBounds[4];" | "uniform " JUCE_HIGHP " float textureBounds[4];" | ||||
| "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;" | |||||
| "varying " JUCE_HIGHP " vec2 pixelPos;" | "varying " JUCE_HIGHP " vec2 pixelPos;" | ||||
| "void main()" | "void main()" | ||||
| "{" | "{" | ||||
| JUCE_HIGHP " vec2 texturePos = (pixelPos - vec2 (textureBounds[0], textureBounds[1]))" | JUCE_HIGHP " vec2 texturePos = (pixelPos - vec2 (textureBounds[0], textureBounds[1]))" | ||||
| "/ vec2 (textureBounds[2], textureBounds[3]);" | "/ 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); | GL_FRAGMENT_SHADER); | ||||
| prog.link(); | prog.link(); | ||||
| @@ -740,19 +742,23 @@ void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea, | |||||
| : positionAttribute (prog, "position"), | : positionAttribute (prog, "position"), | ||||
| screenSize (prog, "screenSize"), | screenSize (prog, "screenSize"), | ||||
| imageTexture (prog, "imageTexture"), | 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() }; | const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() }; | ||||
| textureBounds.set (m, 4); | textureBounds.set (m, 4); | ||||
| imageTexture.set (0); | imageTexture.set (0); | ||||
| screenSize.set (targetWidth, targetHeight); | screenSize.set (targetWidth, targetHeight); | ||||
| vOffsetAndScale.set (flippedVertically ? 0.0f : 1.0f, | |||||
| flippedVertically ? 1.0f : -1.0f); | |||||
| } | } | ||||
| OpenGLShaderProgram::Attribute positionAttribute; | OpenGLShaderProgram::Attribute positionAttribute; | ||||
| OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds; | |||||
| OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale; | |||||
| }; | }; | ||||
| OpenGLShaderProgram program; | 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 GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top }; | ||||
| const OverlayShaderProgram& program = OverlayShaderProgram::select (*this); | 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.glVertexAttribPointer (program.params.positionAttribute.attributeID, 2, GL_SHORT, GL_FALSE, 4, vertices); | ||||
| extensions.glEnableVertexAttribArray (program.params.positionAttribute.attributeID); | extensions.glEnableVertexAttribArray (program.params.positionAttribute.attributeID); | ||||
| @@ -219,10 +219,13 @@ public: | |||||
| used for scaling of the coordinates. | used for scaling of the coordinates. | ||||
| @param contextHeight the height of the context or framebuffer that is being drawn into, | @param contextHeight the height of the context or framebuffer that is being drawn into, | ||||
| used for vertical flipping of the y coordinates. | 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, | void copyTexture (const Rectangle<int>& targetClipArea, | ||||
| const Rectangle<int>& anchorPosAndTextureSize, | const Rectangle<int>& anchorPosAndTextureSize, | ||||
| int contextWidth, int contextHeight); | |||||
| int contextWidth, int contextHeight, | |||||
| bool textureOriginIsBottomLeft); | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -222,7 +222,7 @@ bool OpenGLFrameBuffer::initialise (OpenGLFrameBuffer& other) | |||||
| clearGLError(); | clearGLError(); | ||||
| #endif | #endif | ||||
| glBindTexture (GL_TEXTURE_2D, p->textureID); | 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); | glBindTexture (GL_TEXTURE_2D, 0); | ||||
| JUCE_CHECK_OPENGL_ERROR | 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(), | glReadPixels (area.getX(), area.getY(), area.getWidth(), area.getHeight(), | ||||
| JUCE_RGBA_FORMAT, GL_UNSIGNED_BYTE, target); | JUCE_RGBA_FORMAT, GL_UNSIGNED_BYTE, target); | ||||
| pimpl->context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, 0); | pimpl->context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, 0); | ||||
| glPixelStorei (GL_PACK_ALIGNMENT, 0); | |||||
| JUCE_CHECK_OPENGL_ERROR | JUCE_CHECK_OPENGL_ERROR | ||||
| return true; | return true; | ||||
| } | } | ||||
| bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int>& area) | bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int>& area) | ||||
| { | { | ||||
| OpenGLTargetSaver ts (pimpl->context); | |||||
| if (! makeCurrentRenderingTarget()) | if (! makeCurrentRenderingTarget()) | ||||
| return false; | return false; | ||||
| @@ -339,10 +340,10 @@ bool OpenGLFrameBuffer::writePixels (const PixelARGB* data, const Rectangle<int> | |||||
| glDisable (GL_BLEND); | glDisable (GL_BLEND); | ||||
| JUCE_CHECK_OPENGL_ERROR | JUCE_CHECK_OPENGL_ERROR | ||||
| #if JUCE_OPENGL_ES && JUCE_USE_OPENGL_FIXED_FUNCTION | |||||
| OpenGLTexture tex; | OpenGLTexture tex; | ||||
| tex.loadARGBFlipped (data, area.getWidth(), area.getHeight()); | tex.loadARGBFlipped (data, area.getWidth(), area.getHeight()); | ||||
| #if JUCE_OPENGL_ES && JUCE_USE_OPENGL_FIXED_FUNCTION | |||||
| const int texH = tex.getHeight(); | const int texH = tex.getHeight(); | ||||
| tex.bind(); | tex.bind(); | ||||
| const GLint cropRect[4] = { 0, texH - area.getHeight(), area.getWidth(), area.getHeight() }; | 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()); | glDrawTexiOES (area.getX(), area.getY(), 1, area.getWidth(), area.getHeight()); | ||||
| glBindTexture (GL_TEXTURE_2D, 0); | glBindTexture (GL_TEXTURE_2D, 0); | ||||
| #else | #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 | #endif | ||||
| pimpl->context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, 0); | |||||
| JUCE_CHECK_OPENGL_ERROR | JUCE_CHECK_OPENGL_ERROR | ||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -1364,7 +1364,7 @@ public: | |||||
| clip (other.clip), | clip (other.clip), | ||||
| maskArea (other.clip) | maskArea (other.clip) | ||||
| { | { | ||||
| TargetSaver ts (state.target.context); | |||||
| OpenGLTargetSaver ts (state.target.context); | |||||
| state.currentShader.clearShader (state.shaderQuadQueue); | state.currentShader.clearShader (state.shaderQuadQueue); | ||||
| state.shaderQuadQueue.flush(); | state.shaderQuadQueue.flush(); | ||||
| state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); | state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); | ||||
| @@ -1393,7 +1393,7 @@ public: | |||||
| clip (r.getBounds()), | clip (r.getBounds()), | ||||
| maskArea (clip) | maskArea (clip) | ||||
| { | { | ||||
| TargetSaver ts (state.target.context); | |||||
| OpenGLTargetSaver ts (state.target.context); | |||||
| state.currentShader.clearShader (state.shaderQuadQueue); | state.currentShader.clearShader (state.shaderQuadQueue); | ||||
| state.shaderQuadQueue.flush(); | state.shaderQuadQueue.flush(); | ||||
| state.activeTextures.clear(); | state.activeTextures.clear(); | ||||
| @@ -1429,7 +1429,7 @@ public: | |||||
| if (excluded.getNumRectangles() == 1) | if (excluded.getNumRectangles() == 1) | ||||
| return excludeClipRectangle (excluded.getRectangle (0)); | return excludeClipRectangle (excluded.getRectangle (0)); | ||||
| TargetSaver ts (state.target.context); | |||||
| OpenGLTargetSaver ts (state.target.context); | |||||
| makeActive(); | makeActive(); | ||||
| state.blendMode.setBlendMode (state.shaderQuadQueue, true); | state.blendMode.setBlendMode (state.shaderQuadQueue, true); | ||||
| state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram); | state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram); | ||||
| @@ -1445,7 +1445,7 @@ public: | |||||
| if (r.contains (clip)) | if (r.contains (clip)) | ||||
| return Ptr(); | return Ptr(); | ||||
| TargetSaver ts (state.target.context); | |||||
| OpenGLTargetSaver ts (state.target.context); | |||||
| makeActive(); | makeActive(); | ||||
| state.blendMode.setBlendMode (state.shaderQuadQueue, true); | state.blendMode.setBlendMode (state.shaderQuadQueue, true); | ||||
| state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram); | state.currentShader.setShader (maskArea, state.shaderQuadQueue, state.currentShader.programs->solidColourProgram); | ||||
| @@ -1460,7 +1460,7 @@ public: | |||||
| if (! et.isEmpty()) | if (! et.isEmpty()) | ||||
| { | { | ||||
| TargetSaver ts (state.target.context); | |||||
| OpenGLTargetSaver ts (state.target.context); | |||||
| state.currentShader.clearShader (state.shaderQuadQueue); | state.currentShader.clearShader (state.shaderQuadQueue); | ||||
| state.shaderQuadQueue.flush(); | state.shaderQuadQueue.flush(); | ||||
| state.activeTextures.clear(); | state.activeTextures.clear(); | ||||
| @@ -1480,7 +1480,7 @@ public: | |||||
| if (clip.isEmpty()) | if (clip.isEmpty()) | ||||
| return Ptr(); | return Ptr(); | ||||
| TargetSaver ts (state.target.context); | |||||
| OpenGLTargetSaver ts (state.target.context); | |||||
| makeActive(); | makeActive(); | ||||
| state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); | state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); | ||||
| @@ -1501,7 +1501,7 @@ public: | |||||
| Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) | Ptr clipToImageAlpha (const OpenGLTextureFromImage& image, const AffineTransform& transform) | ||||
| { | { | ||||
| TargetSaver ts (state.target.context); | |||||
| OpenGLTargetSaver ts (state.target.context); | |||||
| makeActive(); | makeActive(); | ||||
| state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); | state.activeTextures.setSingleTextureMode (state.shaderQuadQueue); | ||||
| state.activeTextures.bindTexture (image.textureID); | state.activeTextures.bindTexture (image.textureID); | ||||
| @@ -1617,28 +1617,6 @@ private: | |||||
| JUCE_DECLARE_NON_COPYABLE (ShaderFillOperation) | 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() | void makeActive() | ||||
| { | { | ||||
| state.shaderQuadQueue.flush(); | state.shaderQuadQueue.flush(); | ||||
| @@ -2209,7 +2187,8 @@ public: | |||||
| target.makeActive(); | target.makeActive(); | ||||
| target.context.copyTexture (target.bounds, Rectangle<int> (texture.getWidth(), | target.context.copyTexture (target.bounds, Rectangle<int> (texture.getWidth(), | ||||
| texture.getHeight()), | texture.getHeight()), | ||||
| target.bounds.getWidth(), target.bounds.getHeight()); | |||||
| target.bounds.getWidth(), target.bounds.getHeight(), | |||||
| false); | |||||
| glBindTexture (GL_TEXTURE_2D, 0); | glBindTexture (GL_TEXTURE_2D, 0); | ||||
| #if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
| @@ -38,7 +38,7 @@ bool OpenGLTexture::isValidSize (int width, int height) | |||||
| return isPowerOfTwo (width) && isPowerOfTwo (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(); | 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. | // context. You'll need to create this object in one of the OpenGLContext's callbacks. | ||||
| jassert (ownerContext != nullptr); | jassert (ownerContext != nullptr); | ||||
| jassert (isValidSize (w, h)); // Perhaps these dimensions must be a power-of-two? | |||||
| width = w; | |||||
| height = h; | |||||
| if (textureID == 0) | if (textureID == 0) | ||||
| { | { | ||||
| JUCE_CHECK_OPENGL_ERROR | 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); | glPixelStorei (GL_UNPACK_ALIGNMENT, 1); | ||||
| JUCE_CHECK_OPENGL_ERROR | 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 | JUCE_CHECK_OPENGL_ERROR | ||||
| } | } | ||||
| @@ -79,29 +92,20 @@ template <class PixelType> | |||||
| struct Flipper | struct Flipper | ||||
| { | { | ||||
| static void flip (HeapBlock<PixelARGB>& dataCopy, const uint8* srcData, const int lineStride, | 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) | for (int y = 0; y < h; ++y) | ||||
| { | { | ||||
| const PixelType* src = (const PixelType*) srcData; | 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) | for (int x = 0; x < w; ++x) | ||||
| dst[x].set (src[x]); | dst[x].set (src[x]); | ||||
| if (textureW > w) | |||||
| dst[w].set (PixelARGB (0)); | |||||
| srcData += lineStride; | 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 imageW = image.getWidth(); | ||||
| const int imageH = image.getHeight(); | const int imageH = image.getHeight(); | ||||
| const int textureW = nextPowerOfTwo (imageW); | |||||
| const int textureH = nextPowerOfTwo (imageH); | |||||
| HeapBlock<PixelARGB> dataCopy; | HeapBlock<PixelARGB> dataCopy; | ||||
| Image::BitmapData srcData (image, Image::BitmapData::readOnly); | Image::BitmapData srcData (image, Image::BitmapData::readOnly); | ||||
| switch (srcData.pixelFormat) | 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; | 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) | 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) | 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) | void OpenGLTexture::loadARGBFlipped (const PixelARGB* pixels, int w, int h) | ||||
| { | { | ||||
| const int textureW = nextPowerOfTwo (w); | |||||
| const int textureH = nextPowerOfTwo (h); | |||||
| HeapBlock<PixelARGB> flippedCopy; | 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() | void OpenGLTexture::release() | ||||
| @@ -47,11 +47,10 @@ public: | |||||
| void loadImage (const Image& image); | void loadImage (const Image& image); | ||||
| /** Creates a texture from a raw array of pixels. | /** 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, | 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). | 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); | void loadARGB (const PixelARGB* pixels, int width, int height); | ||||
| @@ -63,11 +62,10 @@ public: | |||||
| void loadARGBFlipped (const PixelARGB* pixels, int width, int height); | void loadARGBFlipped (const PixelARGB* pixels, int width, int height); | ||||
| /** Creates an alpha-channel texture from an array of alpha values. | /** 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, | 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). | 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); | void loadAlpha (const uint8* pixels, int width, int height); | ||||
| @@ -112,7 +110,7 @@ private: | |||||
| int width, height; | int width, height; | ||||
| OpenGLContext* ownerContext; | 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) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture) | ||||
| }; | }; | ||||