From f31dca5f2f2bccd73752af0c7e45fcb6c7326d2d Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 10 Oct 2011 18:30:51 +0100 Subject: [PATCH] More OpenGL work. --- .../Juce Demo.xcodeproj/project.pbxproj | 2 + .../Builds/VisualStudio2005/Juce Demo.vcproj | 1 + .../Builds/VisualStudio2008/Juce Demo.vcproj | 1 + .../Builds/VisualStudio2010/Juce Demo.vcxproj | 1 + .../Juce Demo.vcxproj.filters | 3 + .../iOS/Juce Demo.xcodeproj/project.pbxproj | 2 + .../Plugin Host.xcodeproj/project.pbxproj | 2 + .../VisualStudio2005/Plugin Host.vcproj | 1 + .../VisualStudio2008/Plugin Host.vcproj | 1 + .../MacOSX/juce.xcodeproj/project.pbxproj | 2 + .../Builds/VisualStudio2008/juce.vcproj | 1 + .../Builds/VisualStudio2010/juce.vcxproj | 1 + .../VisualStudio2010/juce.vcxproj.filters | 3 + modules/juce_core/maths/juce_MathsFunctions.h | 9 + .../geometry/juce_AffineTransform.cpp | 9 +- .../geometry/juce_AffineTransform.h | 5 + modules/juce_graphics/images/juce_Image.cpp | 11 +- .../native/juce_mac_CoreGraphicsContext.mm | 7 +- modules/juce_opengl/juce_opengl.h | 4 + .../opengl/juce_OpenGLComponent.cpp | 13 +- .../juce_opengl/opengl/juce_OpenGLComponent.h | 29 +- .../juce_opengl/opengl/juce_OpenGLContext.h | 2 +- .../opengl/juce_OpenGLFrameBuffer.cpp | 100 +++++-- .../opengl/juce_OpenGLFrameBuffer.h | 26 +- .../juce_opengl/opengl/juce_OpenGLHelpers.cpp | 283 +++++++++++++++++- .../juce_opengl/opengl/juce_OpenGLHelpers.h | 35 ++- .../juce_opengl/opengl/juce_OpenGLImage.cpp | 4 +- .../opengl/juce_OpenGLRenderingTarget.h | 62 ++++ .../juce_opengl/opengl/juce_OpenGLTexture.cpp | 29 +- .../juce_opengl/opengl/juce_OpenGLTexture.h | 12 +- 30 files changed, 561 insertions(+), 100 deletions(-) create mode 100644 modules/juce_opengl/opengl/juce_OpenGLRenderingTarget.h diff --git a/extras/JuceDemo/Builds/MacOSX/Juce Demo.xcodeproj/project.pbxproj b/extras/JuceDemo/Builds/MacOSX/Juce Demo.xcodeproj/project.pbxproj index b827dc2d31..699e6a5f43 100644 --- a/extras/JuceDemo/Builds/MacOSX/Juce Demo.xcodeproj/project.pbxproj +++ b/extras/JuceDemo/Builds/MacOSX/Juce Demo.xcodeproj/project.pbxproj @@ -558,6 +558,7 @@ 936C6419223D7B28CE16A72B = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_BufferedInputStream.cpp"; path = "../../../../modules/juce_core/streams/juce_BufferedInputStream.cpp"; sourceTree = "SOURCE_ROOT"; }; 9398049BFCAC42B8FD1E16C5 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_DirectXPluginFormat.h"; path = "../../../../modules/juce_audio_processors/format_types/juce_DirectXPluginFormat.h"; sourceTree = "SOURCE_ROOT"; }; 93C594ECD5CEA7878114149A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_win32_WebBrowserComponent.cpp"; path = "../../../../modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; + 942191F317B1B2B1CFBB7C60 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_OpenGLRenderingTarget.h"; path = "../../../../modules/juce_opengl/opengl/juce_OpenGLRenderingTarget.h"; sourceTree = "SOURCE_ROOT"; }; 94A9A7DF00C64509E9B5A337 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = QuickTimeDemo.cpp; path = ../../Source/demos/QuickTimeDemo.cpp; sourceTree = "SOURCE_ROOT"; }; 94EB4630C360BA31195FA13D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_AudioIODeviceType.cpp"; path = "../../../../modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp"; sourceTree = "SOURCE_ROOT"; }; 95305C4003CD4784CA20D1D1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_TextEditorKeyMapper.h"; path = "../../../../modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h"; sourceTree = "SOURCE_ROOT"; }; @@ -1871,6 +1872,7 @@ E46977801F19277F4D3B324B, 17422D876DAAF091ED089B8D, B81FA2ED22BCC03D124031ED, + 942191F317B1B2B1CFBB7C60, FE9A5898294228EB7F439951, 4CFB262AA9778621B8B33A7D ); name = opengl; sourceTree = ""; }; C92FEA88254DAAE793DB3CF7 = { isa = PBXGroup; children = ( diff --git a/extras/JuceDemo/Builds/VisualStudio2005/Juce Demo.vcproj b/extras/JuceDemo/Builds/VisualStudio2005/Juce Demo.vcproj index e47e49f208..1e614143b4 100644 --- a/extras/JuceDemo/Builds/VisualStudio2005/Juce Demo.vcproj +++ b/extras/JuceDemo/Builds/VisualStudio2005/Juce Demo.vcproj @@ -4242,6 +4242,7 @@ + diff --git a/extras/JuceDemo/Builds/VisualStudio2008/Juce Demo.vcproj b/extras/JuceDemo/Builds/VisualStudio2008/Juce Demo.vcproj index b4e4e41172..50342d31b4 100644 --- a/extras/JuceDemo/Builds/VisualStudio2008/Juce Demo.vcproj +++ b/extras/JuceDemo/Builds/VisualStudio2008/Juce Demo.vcproj @@ -4242,6 +4242,7 @@ + diff --git a/extras/JuceDemo/Builds/VisualStudio2010/Juce Demo.vcxproj b/extras/JuceDemo/Builds/VisualStudio2010/Juce Demo.vcxproj index f4349c353a..bfe1e27058 100644 --- a/extras/JuceDemo/Builds/VisualStudio2010/Juce Demo.vcxproj +++ b/extras/JuceDemo/Builds/VisualStudio2010/Juce Demo.vcxproj @@ -1580,6 +1580,7 @@ + diff --git a/extras/JuceDemo/Builds/VisualStudio2010/Juce Demo.vcxproj.filters b/extras/JuceDemo/Builds/VisualStudio2010/Juce Demo.vcxproj.filters index 672669dfd6..0a6ac9f75a 100644 --- a/extras/JuceDemo/Builds/VisualStudio2010/Juce Demo.vcxproj.filters +++ b/extras/JuceDemo/Builds/VisualStudio2010/Juce Demo.vcxproj.filters @@ -2781,6 +2781,9 @@ Juce Modules\juce_opengl\opengl + + Juce Modules\juce_opengl\opengl + Juce Modules\juce_opengl\opengl diff --git a/extras/JuceDemo/Builds/iOS/Juce Demo.xcodeproj/project.pbxproj b/extras/JuceDemo/Builds/iOS/Juce Demo.xcodeproj/project.pbxproj index 1372c9a03a..3daf4bf1c6 100644 --- a/extras/JuceDemo/Builds/iOS/Juce Demo.xcodeproj/project.pbxproj +++ b/extras/JuceDemo/Builds/iOS/Juce Demo.xcodeproj/project.pbxproj @@ -550,6 +550,7 @@ 936C6419223D7B28CE16A72B = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_BufferedInputStream.cpp"; path = "../../../../modules/juce_core/streams/juce_BufferedInputStream.cpp"; sourceTree = "SOURCE_ROOT"; }; 9398049BFCAC42B8FD1E16C5 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_DirectXPluginFormat.h"; path = "../../../../modules/juce_audio_processors/format_types/juce_DirectXPluginFormat.h"; sourceTree = "SOURCE_ROOT"; }; 93C594ECD5CEA7878114149A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_win32_WebBrowserComponent.cpp"; path = "../../../../modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; + 942191F317B1B2B1CFBB7C60 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_OpenGLRenderingTarget.h"; path = "../../../../modules/juce_opengl/opengl/juce_OpenGLRenderingTarget.h"; sourceTree = "SOURCE_ROOT"; }; 94A9A7DF00C64509E9B5A337 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = QuickTimeDemo.cpp; path = ../../Source/demos/QuickTimeDemo.cpp; sourceTree = "SOURCE_ROOT"; }; 94EB4630C360BA31195FA13D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_AudioIODeviceType.cpp"; path = "../../../../modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp"; sourceTree = "SOURCE_ROOT"; }; 95305C4003CD4784CA20D1D1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_TextEditorKeyMapper.h"; path = "../../../../modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h"; sourceTree = "SOURCE_ROOT"; }; @@ -1861,6 +1862,7 @@ E46977801F19277F4D3B324B, 17422D876DAAF091ED089B8D, B81FA2ED22BCC03D124031ED, + 942191F317B1B2B1CFBB7C60, FE9A5898294228EB7F439951, 4CFB262AA9778621B8B33A7D ); name = opengl; sourceTree = ""; }; C92FEA88254DAAE793DB3CF7 = { isa = PBXGroup; children = ( diff --git a/extras/audio plugin host/Builds/MacOSX/Plugin Host.xcodeproj/project.pbxproj b/extras/audio plugin host/Builds/MacOSX/Plugin Host.xcodeproj/project.pbxproj index 7592062567..90e81977e4 100644 --- a/extras/audio plugin host/Builds/MacOSX/Plugin Host.xcodeproj/project.pbxproj +++ b/extras/audio plugin host/Builds/MacOSX/Plugin Host.xcodeproj/project.pbxproj @@ -341,6 +341,7 @@ 582D00AF5D2B03D99D395C5D = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_LowLevelGraphicsSoftwareRenderer.h"; path = "../../../../modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h"; sourceTree = "SOURCE_ROOT"; }; 5855BA9AD11D9A3AE9FAF972 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_Result.h"; path = "../../../../modules/juce_core/misc/juce_Result.h"; sourceTree = "SOURCE_ROOT"; }; 589769409FB8620A45B85335 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_LowLevelGraphicsPostScriptRenderer.h"; path = "../../../../modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h"; sourceTree = "SOURCE_ROOT"; }; + 597D5688E23397FE09F53BA3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_OpenGLRenderingTarget.h"; path = "../../../../modules/juce_opengl/opengl/juce_OpenGLRenderingTarget.h"; sourceTree = "SOURCE_ROOT"; }; 599F30CF78998EA6657D56A0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_MixerAudioSource.cpp"; path = "../../../../modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp"; sourceTree = "SOURCE_ROOT"; }; 5A835BB9C7C51847701951A1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_OutputStream.h"; path = "../../../../modules/juce_core/streams/juce_OutputStream.h"; sourceTree = "SOURCE_ROOT"; }; 5AA6D24CBDEC48447451F46A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "juce_gui_extra.mm"; path = "../../../../modules/juce_gui_extra/juce_gui_extra.mm"; sourceTree = "SOURCE_ROOT"; }; @@ -1793,6 +1794,7 @@ C0EECABDA5446EAD76E2578F, 4CE76ABC51CC5E019AED3C2B, 8BA1AF085755958342C9213A, + 597D5688E23397FE09F53BA3, 651055751E902F40B83EFD55, 09089786B542AEB678DDE38F ); name = opengl; sourceTree = ""; }; E8C88E6E3FEC2E7A4373205B = { isa = PBXGroup; children = ( diff --git a/extras/audio plugin host/Builds/VisualStudio2005/Plugin Host.vcproj b/extras/audio plugin host/Builds/VisualStudio2005/Plugin Host.vcproj index 181cf71dd9..4872860b44 100644 --- a/extras/audio plugin host/Builds/VisualStudio2005/Plugin Host.vcproj +++ b/extras/audio plugin host/Builds/VisualStudio2005/Plugin Host.vcproj @@ -4165,6 +4165,7 @@ + diff --git a/extras/audio plugin host/Builds/VisualStudio2008/Plugin Host.vcproj b/extras/audio plugin host/Builds/VisualStudio2008/Plugin Host.vcproj index 0ac6d062e0..2ade11601e 100644 --- a/extras/audio plugin host/Builds/VisualStudio2008/Plugin Host.vcproj +++ b/extras/audio plugin host/Builds/VisualStudio2008/Plugin Host.vcproj @@ -4165,6 +4165,7 @@ + diff --git a/extras/static library/Builds/MacOSX/juce.xcodeproj/project.pbxproj b/extras/static library/Builds/MacOSX/juce.xcodeproj/project.pbxproj index 98c7bf18d0..05d502dc4b 100644 --- a/extras/static library/Builds/MacOSX/juce.xcodeproj/project.pbxproj +++ b/extras/static library/Builds/MacOSX/juce.xcodeproj/project.pbxproj @@ -498,6 +498,7 @@ 9D4FC9BEE122256B2557D088 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_OpenGLFrameBuffer.h"; path = "../../../../modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h"; sourceTree = "SOURCE_ROOT"; }; 9D7F83D461E62DDA82C74EA8 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_PropertyPanel.cpp"; path = "../../../../modules/juce_gui_basics/properties/juce_PropertyPanel.cpp"; sourceTree = "SOURCE_ROOT"; }; 9D9D7797A0B32F28838D8FC7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_GZIPCompressorOutputStream.h"; path = "../../../../modules/juce_core/zip/juce_GZIPCompressorOutputStream.h"; sourceTree = "SOURCE_ROOT"; }; + 9E067C6101B6E2D11365EBDC = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_OpenGLRenderingTarget.h"; path = "../../../../modules/juce_opengl/opengl/juce_OpenGLRenderingTarget.h"; sourceTree = "SOURCE_ROOT"; }; 9E7E5C859FFDA600253B3379 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ScopedPointer.h"; path = "../../../../modules/juce_core/memory/juce_ScopedPointer.h"; sourceTree = "SOURCE_ROOT"; }; 9E80E6825A34DD1E15D0F61C = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_HeapBlock.h"; path = "../../../../modules/juce_core/memory/juce_HeapBlock.h"; sourceTree = "SOURCE_ROOT"; }; 9EAEFA0E956265487E7A3919 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_ios_Audio.cpp"; path = "../../../../modules/juce_audio_devices/native/juce_ios_Audio.cpp"; sourceTree = "SOURCE_ROOT"; }; @@ -1712,6 +1713,7 @@ 6DCD8800E380DB691DC9D467, 7251827C30AC1C4F55C3DB69, 6D95302F8D68BA3F0538214F, + 9E067C6101B6E2D11365EBDC, A0D54F01BC8018CC41FA34E9, 53B9B6090EDB9B6E0FA8577F ); name = opengl; sourceTree = ""; }; 637D3A9082C214CA0E067538 = { isa = PBXGroup; children = ( diff --git a/extras/static library/Builds/VisualStudio2008/juce.vcproj b/extras/static library/Builds/VisualStudio2008/juce.vcproj index c64e6debf3..d5ade7664f 100644 --- a/extras/static library/Builds/VisualStudio2008/juce.vcproj +++ b/extras/static library/Builds/VisualStudio2008/juce.vcproj @@ -4056,6 +4056,7 @@ + diff --git a/extras/static library/Builds/VisualStudio2010/juce.vcxproj b/extras/static library/Builds/VisualStudio2010/juce.vcxproj index 9fe7e2c341..2ad25436a5 100644 --- a/extras/static library/Builds/VisualStudio2010/juce.vcxproj +++ b/extras/static library/Builds/VisualStudio2010/juce.vcxproj @@ -1526,6 +1526,7 @@ + diff --git a/extras/static library/Builds/VisualStudio2010/juce.vcxproj.filters b/extras/static library/Builds/VisualStudio2010/juce.vcxproj.filters index 70439dbfce..df674c260c 100644 --- a/extras/static library/Builds/VisualStudio2010/juce.vcxproj.filters +++ b/extras/static library/Builds/VisualStudio2010/juce.vcxproj.filters @@ -2616,6 +2616,9 @@ Juce Modules\juce_opengl\opengl + + Juce Modules\juce_opengl\opengl + Juce Modules\juce_opengl\opengl diff --git a/modules/juce_core/maths/juce_MathsFunctions.h b/modules/juce_core/maths/juce_MathsFunctions.h index 60c2c41c14..17d4f8ce7f 100644 --- a/modules/juce_core/maths/juce_MathsFunctions.h +++ b/modules/juce_core/maths/juce_MathsFunctions.h @@ -415,6 +415,15 @@ inline int roundFloatToInt (const float value) noexcept return roundToInt (value); } +//============================================================================== +/** Returns true if the specified integer is a power-of-two. +*/ +template +bool isPowerOfTwo (IntegerType value) +{ + return (value & (value - 1)) == 0; +} + //============================================================================== #if (JUCE_INTEL && JUCE_32BIT) || defined (DOXYGEN) /** This macro can be applied to a float variable to check whether it contains a denormalised diff --git a/modules/juce_graphics/geometry/juce_AffineTransform.cpp b/modules/juce_graphics/geometry/juce_AffineTransform.cpp index 8578021ba8..6accbd5709 100644 --- a/modules/juce_graphics/geometry/juce_AffineTransform.cpp +++ b/modules/juce_graphics/geometry/juce_AffineTransform.cpp @@ -157,14 +157,14 @@ AffineTransform AffineTransform::scale (const float factorX, const float factorY } AffineTransform AffineTransform::scaled (const float factorX, const float factorY, - const float pivotX, const float pivotY) const noexcept + const float pivotX, const float pivotY) const noexcept { return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02 + pivotX * (1.0f - factorX), factorY * mat10, factorY * mat11, factorY * mat12 + pivotY * (1.0f - factorY)); } AffineTransform AffineTransform::scale (const float factorX, const float factorY, - const float pivotX, const float pivotY) noexcept + const float pivotX, const float pivotY) noexcept { return AffineTransform (factorX, 0, pivotX * (1.0f - factorX), 0, factorY, pivotY * (1.0f - factorY)); @@ -186,6 +186,11 @@ AffineTransform AffineTransform::sheared (const float shearX, const float shearY shearY * mat02 + mat12); } +AffineTransform AffineTransform::verticalFlip (const float height) noexcept +{ + return AffineTransform (1.0f, 0, 0, 0, -1.0f, height); +} + AffineTransform AffineTransform::inverted() const noexcept { double determinant = (mat00 * mat11 - mat10 * mat01); diff --git a/modules/juce_graphics/geometry/juce_AffineTransform.h b/modules/juce_graphics/geometry/juce_AffineTransform.h index f6a5bf6b9f..984c992e5b 100644 --- a/modules/juce_graphics/geometry/juce_AffineTransform.h +++ b/modules/juce_graphics/geometry/juce_AffineTransform.h @@ -185,6 +185,11 @@ public: /** Returns a shear transform, centred around the origin (0, 0). */ static AffineTransform shear (float shearX, float shearY) noexcept; + /** Returns a transform that will flip co-ordinates vertically within a window of the given height. + This is handy for converting between upside-down coordinate systems such as OpenGL or CoreGraphics. + */ + static AffineTransform verticalFlip (float height) noexcept; + /** Returns a matrix which is the inverse operation of this one. Some matrices don't have an inverse - in this case, the method will just return diff --git a/modules/juce_graphics/images/juce_Image.cpp b/modules/juce_graphics/images/juce_Image.cpp index e75c36dfb1..1e31eb8c9f 100644 --- a/modules/juce_graphics/images/juce_Image.cpp +++ b/modules/juce_graphics/images/juce_Image.cpp @@ -116,7 +116,16 @@ public: Image::SharedImage* clone() { - return new SubsectionSharedImage (image->clone(), area); + Image newImage (format, area.getWidth(), area.getHeight(), + format != Image::RGB, image->getType()); + + { + Graphics g (newImage); + g.drawImageAt (Image (this), 0, 0); + } + + newImage.getSharedImage()->incReferenceCount(); + return newImage.getSharedImage(); } private: diff --git a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index a390234316..811b1d8cbd 100644 --- a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -169,8 +169,7 @@ void CoreGraphicsContext::setOrigin (int x, int y) void CoreGraphicsContext::addTransform (const AffineTransform& transform) { - applyTransform (AffineTransform::scale (1.0f, -1.0f) - .translated (0, flipHeight) + applyTransform (AffineTransform::verticalFlip (flipHeight) .followedBy (transform) .translated (0, -flipHeight) .scaled (1.0f, -1.0f)); @@ -253,7 +252,7 @@ void CoreGraphicsContext::clipToImageAlpha (const Image& sourceImage, const Affi CGImageRef image = CoreGraphicsImage::createImage (singleChannelImage, true, greyColourSpace, true); flip(); - AffineTransform t (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + AffineTransform t (AffineTransform::verticalFlip (sourceImage.getHeight()).followedBy (transform)); applyTransform (t); CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); @@ -447,7 +446,7 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans CGContextSetAlpha (context, state->fillType.getOpacity()); flip(); - applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, ih).followedBy (transform)); + applyTransform (AffineTransform::verticalFlip (ih).followedBy (transform)); CGRect imageRect = CGRectMake (0, 0, iw, ih); if (fillEntireClipAsTiles) diff --git a/modules/juce_opengl/juce_opengl.h b/modules/juce_opengl/juce_opengl.h index 60c9ccec7b..92b616588d 100644 --- a/modules/juce_opengl/juce_opengl.h +++ b/modules/juce_opengl/juce_opengl.h @@ -81,6 +81,7 @@ //============================================================================= BEGIN_JUCE_NAMESPACE +#include "opengl/juce_OpenGLRenderingTarget.h" // START_AUTOINCLUDE opengl #ifndef __JUCE_OPENGLCOMPONENT_JUCEHEADER__ @@ -101,6 +102,9 @@ BEGIN_JUCE_NAMESPACE #ifndef __JUCE_OPENGLPIXELFORMAT_JUCEHEADER__ #include "opengl/juce_OpenGLPixelFormat.h" #endif +#ifndef __JUCE_OPENGLRENDERINGTARGET_JUCEHEADER__ + #include "opengl/juce_OpenGLRenderingTarget.h" +#endif #ifndef __JUCE_OPENGLTEXTURE_JUCEHEADER__ #include "opengl/juce_OpenGLTexture.h" #endif diff --git a/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp b/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp index 80d8a9839c..33559ca28f 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLComponent.cpp @@ -121,7 +121,6 @@ OpenGLContext* OpenGLContext::getCurrentContext() class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher { public: - //============================================================================== OpenGLComponentWatcher (OpenGLComponent* const owner_) : ComponentMovementWatcher (owner_), owner (owner_) @@ -145,7 +144,6 @@ public: owner->stopBackgroundThread(); } - //============================================================================== private: OpenGLComponent* const owner; @@ -276,22 +274,17 @@ void OpenGLComponent::recreateContextAsync() repaint(); } -bool OpenGLComponent::makeCurrentContextActive() +bool OpenGLComponent::makeCurrentRenderingTarget() { return context != nullptr && context->makeActive(); } -void OpenGLComponent::makeCurrentContextInactive() +void OpenGLComponent::releaseAsRenderingTarget() { if (context != nullptr) context->makeInactive(); } -bool OpenGLComponent::isActiveContext() const noexcept -{ - return context != nullptr && context->isActive(); -} - void OpenGLComponent::swapBuffers() { if (context != nullptr) @@ -416,7 +409,7 @@ bool OpenGLComponent::renderAndSwapBuffers() if (context != nullptr) { - if (! makeCurrentContextActive()) + if (! makeCurrentRenderingTarget()) return false; if (needToUpdateViewport) diff --git a/modules/juce_opengl/opengl/juce_OpenGLComponent.h b/modules/juce_opengl/opengl/juce_OpenGLComponent.h index ec534c1e91..d3239a3c0b 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLComponent.h +++ b/modules/juce_opengl/opengl/juce_OpenGLComponent.h @@ -42,7 +42,8 @@ method to draw its contents. */ -class JUCE_API OpenGLComponent : public OpenGLBaseType +class JUCE_API OpenGLComponent : public OpenGLBaseType, + public OpenGLRenderingTarget { public: //============================================================================== @@ -160,10 +161,7 @@ public: */ OpenGLContext* getCurrentContext() const noexcept { return context; } - /** Makes this component the current openGL context. - - You might want to use this in things like your resize() method, before calling - GL commands. + /** Makes this component the currently active openGL context. If this returns false, then the context isn't active, so you should avoid making any calls. @@ -172,25 +170,18 @@ public: it does this, it will also synchronously call the newOpenGLContextCreated() method to let you initialise it as necessary. - @see OpenGLContext::makeActive + @see releaseAsRenderingTarget */ - bool makeCurrentContextActive(); + bool makeCurrentRenderingTarget(); /** Stops the current component being the active OpenGL context. - - This is the opposite of makeCurrentContextActive() - - @see OpenGLContext::makeInactive - */ - void makeCurrentContextInactive(); - - /** Returns true if this component's context is the active openGL context for the - current thread. - - @see OpenGLContext::isActive + This is the opposite of makeCurrentRenderingTarget() + @see makeCurrentRenderingTarget */ - bool isActiveContext() const noexcept; + void releaseAsRenderingTarget(); + int getRenderingTargetWidth() const { return getWidth(); } + int getRenderingTargetHeight() const { return getHeight(); } //============================================================================== /** Calls the rendering callback, and swaps the buffers afterwards. diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.h b/modules/juce_opengl/opengl/juce_OpenGLContext.h index be1c4f6e53..4e738ee85c 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.h +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.h @@ -85,7 +85,7 @@ public: /** Returns an OS-dependent handle to the raw GL context. - On win32, this will be a HGLRC; on the Mac, an AGLContext; on Linux, + On win32, this will be a HGLRC; on the Mac, an NSOpenGLContext; on Linux, a GLXContext. */ virtual void* getRawContext() const noexcept = 0; diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp index a1368eab05..03f6f810ae 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.cpp @@ -258,14 +258,14 @@ public: : width (w), height (h), data (w * h) { - buffer.readPixels (data, 0, Rectangle (0, 0, w, h)); + buffer.readPixels (data, Rectangle (0, 0, w, h)); } bool restore (OpenGLFrameBuffer& buffer) { if (buffer.initialise (width, height)) { - buffer.writePixels (data, 0, 4, Rectangle (0, 0, width, height)); + buffer.writePixels (data, 4, Rectangle (0, 0, width, height)); return true; } @@ -294,13 +294,18 @@ bool OpenGLFrameBuffer::initialise (int width, int height) return pimpl != nullptr; } -bool OpenGLFrameBuffer::initialise (const Image& content) +bool OpenGLFrameBuffer::initialise (const Image& image) { - if (initialise (content.getWidth(), content.getHeight())) + if (initialise (image.getWidth(), image.getHeight())) { - Image::BitmapData bitmap (content, Image::BitmapData::readOnly); - return writePixels (bitmap.data, bitmap.lineStride / bitmap.pixelStride, - bitmap.pixelStride, content.getBounds()); + { + Image::BitmapData bitmap (image, Image::BitmapData::readOnly); + + if (bitmap.lineStride == image.getWidth() * bitmap.pixelStride) + return writePixels (bitmap.data, bitmap.pixelStride, image.getBounds()); + } + + return initialise (Image (image.getSharedImage()->clone())); } return false; @@ -340,7 +345,7 @@ int OpenGLFrameBuffer::getWidth() const noexcept { return pimpl != nu int OpenGLFrameBuffer::getHeight() const noexcept { return pimpl != nullptr ? pimpl->height : 0; } GLuint OpenGLFrameBuffer::getTextureID() const noexcept { return pimpl != nullptr ? pimpl->textureID : 0; } -bool OpenGLFrameBuffer::makeCurrentTarget() +bool OpenGLFrameBuffer::makeCurrentRenderingTarget() { // trying to use a framebuffer after saving it with saveAndRelease()! Be sure to call // reloadSavedCopy() to put it back into GPU memory before using it.. @@ -349,7 +354,7 @@ bool OpenGLFrameBuffer::makeCurrentTarget() return pimpl != nullptr && pimpl->bind(); } -void OpenGLFrameBuffer::releaseCurrentTarget() +void OpenGLFrameBuffer::releaseAsRenderingTarget() { if (pimpl != nullptr) pimpl->unbind(); @@ -357,22 +362,28 @@ void OpenGLFrameBuffer::releaseCurrentTarget() void OpenGLFrameBuffer::clear (const Colour& colour) { - if (makeCurrentTarget()) + if (makeCurrentRenderingTarget()) { OpenGLHelpers::clear (colour); - releaseCurrentTarget(); + releaseAsRenderingTarget(); } } -bool OpenGLFrameBuffer::readPixels (void* target, int lineStride, const Rectangle& area) +void OpenGLFrameBuffer::makeCurrentAndClear() { - if (! makeCurrentTarget()) - return false; + if (makeCurrentRenderingTarget()) + { + glClearColor (0, 0, 0, 0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } +} - OpenGLHelpers::prepareFor2D (pimpl->width, pimpl->height); +bool OpenGLFrameBuffer::readPixels (void* target, const Rectangle& area) +{ + if (! makeCurrentRenderingTarget()) + return false; glPixelStorei (GL_PACK_ALIGNMENT, 4); - glPixelStorei (GL_PACK_ROW_LENGTH, lineStride); glReadPixels (area.getX(), area.getY(), area.getWidth(), area.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, target); glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); @@ -380,16 +391,16 @@ bool OpenGLFrameBuffer::readPixels (void* target, int lineStride, const Rectangl return true; } -bool OpenGLFrameBuffer::writePixels (const void* data, int lineStride, int pixelStride, const Rectangle& area) +bool OpenGLFrameBuffer::writePixels (const void* data, int pixelStride, const Rectangle& area) { - if (! makeCurrentTarget()) + if (! makeCurrentRenderingTarget()) return false; - OpenGLHelpers::prepareFor2D (pimpl->width, pimpl->height); - jassert (pixelStride == 3 || pixelStride == 4); // can only handle RGB or ARGB const int format = pixelStride == 3 ? GL_RGB : GL_BGRA_EXT; + const int invertedY = pimpl->height - area.getBottom(); + OpenGLHelpers::prepareFor2D (pimpl->width, pimpl->height); glDisable (GL_DEPTH_TEST); glDisable (GL_BLEND); @@ -405,7 +416,6 @@ bool OpenGLFrameBuffer::writePixels (const void* data, int lineStride, int pixel glBindTexture (GL_TEXTURE_2D, temporaryTexture); glPixelStorei (GL_UNPACK_ALIGNMENT, pixelStride); - glPixelStorei (GL_UNPACK_ROW_LENGTH, lineStride); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, area.getWidth(), area.getHeight(), 0, format, GL_UNSIGNED_BYTE, data); @@ -415,19 +425,19 @@ bool OpenGLFrameBuffer::writePixels (const void* data, int lineStride, int pixel const int cropRect[4] = { 0, 0, area.getWidth(), area.getHeight() }; glTexParameteriv (GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect); - glDrawTexiOES (area.getX(), area.getY(), 1, area.getWidth(), area.getHeight()); + glDrawTexiOES (area.getX(), invertedY, 1, area.getWidth(), area.getHeight()); glBindTexture (GL_TEXTURE_2D, 0); glDeleteTextures (1, &temporaryTexture); } #else - glRasterPos2i (area.getX(), area.getY()); + glRasterPos2i (area.getX(), invertedY); glBindTexture (GL_TEXTURE_2D, 0); glPixelStorei (GL_UNPACK_ALIGNMENT, pixelStride); - glPixelStorei (GL_UNPACK_ROW_LENGTH, lineStride); + glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); glDrawPixels (area.getWidth(), area.getHeight(), format, GL_UNSIGNED_BYTE, data); - #endif + #endif glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0); @@ -462,10 +472,39 @@ void OpenGLFrameBuffer::draw3D (float x1, float y1, float z1, } } +void OpenGLFrameBuffer::drawAt (float x1, float y1) const +{ + if (pimpl != nullptr) + { + glEnable (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, pimpl->textureID); + + glDisableClientState (GL_COLOR_ARRAY); + glDisableClientState (GL_NORMAL_ARRAY); + + const GLfloat vertices[] = { x1, y1, + x1 + pimpl->width, y1, + x1, y1 + pimpl->height, + x1 + pimpl->width, y1 + pimpl->height }; + + const GLfloat textureCoords[] = { 0, 0, 1.0f, 0, 0, 1.0f, 1.0f, 1.0f }; + + glEnableClientState (GL_VERTEX_ARRAY); + glVertexPointer (2, GL_FLOAT, 0, vertices); + + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); + + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + glBindTexture (GL_TEXTURE_2D, 0); + } +} + //============================================================================== -void OpenGLFrameBuffer::createAlphaChannelFromPath (const Path& path, const int oversamplingLevel) +void OpenGLFrameBuffer::createAlphaChannelFromPath (const Path& path, const AffineTransform& transform, + const int oversamplingLevel) { - makeCurrentTarget(); + makeCurrentRenderingTarget(); glEnableClientState (GL_VERTEX_ARRAY); glEnableClientState (GL_TEXTURE_COORD_ARRAY); @@ -473,13 +512,12 @@ void OpenGLFrameBuffer::createAlphaChannelFromPath (const Path& path, const int glDisableClientState (GL_NORMAL_ARRAY); glDisable (GL_TEXTURE_2D); glDisable (GL_DEPTH_TEST); - glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); - OpenGLHelpers::prepareFor2D (getWidth(), getHeight()); - - TriangulatedPath (path).draw (oversamplingLevel); + prepareFor2D(); + TriangulatedPath (path, transform).draw (oversamplingLevel); } END_JUCE_NAMESPACE diff --git a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h index 9ccb0e114c..6601af3af1 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h +++ b/modules/juce_opengl/opengl/juce_OpenGLFrameBuffer.h @@ -31,7 +31,7 @@ /** Creates an openGL frame buffer. */ -class JUCE_API OpenGLFrameBuffer +class JUCE_API OpenGLFrameBuffer : public OpenGLRenderingTarget { public: /** Creates an uninitialised buffer. @@ -81,19 +81,25 @@ public: /** Returns the height of the buffer. */ int getHeight() const noexcept; + int getRenderingTargetWidth() const { return getWidth(); } + int getRenderingTargetHeight() const { return getHeight(); } + /** Returns the texture ID number for using this buffer as a texture. */ - unsigned int getTextureID() const noexcept; + GLuint getTextureID() const noexcept; //============================================================================== /** Selects this buffer as the current OpenGL rendering target. */ - bool makeCurrentTarget(); + bool makeCurrentRenderingTarget(); /** Deselects this buffer as the current OpenGL rendering target. */ - void releaseCurrentTarget(); + void releaseAsRenderingTarget(); /** Clears the framebuffer with the specified colour. */ void clear (const Colour& colour); + /** Selects the framebuffer as the current target, and clears it to transparent. */ + void makeCurrentAndClear(); + /** Draws this framebuffer onto the current context, with the specified corner positions. */ void draw2D (float x1, float y1, float x2, float y2, @@ -108,18 +114,20 @@ public: float x4, float y4, float z4, const Colour& colour) const; + /** Draws the framebuffer at a given position. */ + void drawAt (float x1, float y1) const; + /** Reads an area of pixels from the framebuffer into a 32-bit ARGB pixel array. The lineStride is measured as a number of pixels, not bytes - pass a stride of 0 to indicate a packed array. */ - bool readPixels (void* targetData, int lineStride, const Rectangle& sourceArea); + bool readPixels (void* targetData, const Rectangle& sourceArea); /** Writes an area of pixels into the framebuffer from a specified pixel array. The lineStride is measured as a number of pixels, not bytes - pass a stride of 0 to indicate a packed array. */ - bool writePixels (const void* srcData, - int srcLineStride, int srcPixelStride, + bool writePixels (const void* srcData, int srcPixelStride, const Rectangle& targetArea); /** This will render an anti-aliased path into just the alpha channel of this framebuffer. @@ -131,7 +139,9 @@ public: Calling this will make changes to a lot of openGL state, including colour masks, blend functions, etc */ - void createAlphaChannelFromPath (const Path& path, int oversamplingLevel = 4); + void createAlphaChannelFromPath (const Path& path, + const AffineTransform& transform, + int oversamplingLevel = 4); private: class Pimpl; diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp index f41f8910e2..ed94e09eaf 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp @@ -25,6 +25,7 @@ BEGIN_JUCE_NAMESPACE +//============================================================================== void OpenGLHelpers::resetErrorState() { while (glGetError() != GL_NO_ERROR) {} @@ -44,7 +45,7 @@ void OpenGLHelpers::setColour (const Colour& colour) colour.getFloatBlue(), colour.getFloatAlpha()); } -void OpenGLHelpers::prepareFor2D (int width, int height) +void OpenGLHelpers::prepareFor2D (const int width, const int height) { glMatrixMode (GL_PROJECTION); glLoadIdentity(); @@ -75,6 +76,15 @@ void OpenGLHelpers::setPerspective (double fovy, double aspect, double zNear, do #endif } +void OpenGLHelpers::applyTransform (const AffineTransform& t) +{ + const GLfloat m[] = { t.mat00, t.mat10, 0, 0, + t.mat01, t.mat11, 0, 0, + 0, 0, 1, 0, + t.mat02, t.mat12, 0, 1 }; + glMultMatrixf (m); +} + void OpenGLHelpers::drawQuad2D (float x1, float y1, float x2, float y2, float x3, float y3, @@ -126,6 +136,8 @@ namespace OpenGLGradientHelpers { void drawTriangles (GLenum mode, const GLfloat* vertices, const GLfloat* textureCoords, const int numElements) { + glEnable (GL_BLEND); + glEnable (GL_TEXTURE_2D); glEnableClientState (GL_VERTEX_ARRAY); glEnableClientState (GL_TEXTURE_COORD_ARRAY); glDisableClientState (GL_COLOR_ARRAY); @@ -243,11 +255,6 @@ void OpenGLHelpers::fillRectWithColourGradient (const Rectangle& rect, texture.load (lookup, textureSize, 1); texture.bind(); - if (gradient.isOpaque()) - glDisable (GL_BLEND); - else - glEnable (GL_BLEND); - if (gradient.point1 == gradient.point2) { fillRectWithColour (rect, gradient.getColourAtPosition (1.0)); @@ -267,13 +274,17 @@ void OpenGLHelpers::fillRectWithColour (const Rectangle& rect, const Colour glDisableClientState (GL_TEXTURE_COORD_ARRAY); glDisableClientState (GL_COLOR_ARRAY); glDisableClientState (GL_NORMAL_ARRAY); + setColour (colour); + fillRect (rect); +} +void OpenGLHelpers::fillRect (const Rectangle& rect) +{ const GLfloat vertices[] = { (float) rect.getX(), (float) rect.getY(), (float) rect.getRight(), (float) rect.getY(), (float) rect.getX(), (float) rect.getBottom(), (float) rect.getRight(), (float) rect.getBottom() }; - setColour (colour); glVertexPointer (2, GL_FLOAT, 0, vertices); glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); } @@ -283,11 +294,11 @@ void OpenGLHelpers::fillRectWithColour (const Rectangle& rect, const Colour class TriangulatedPath::TrapezoidedPath { public: - TrapezoidedPath (const Path& p) + TrapezoidedPath (const Path& p, const AffineTransform& transform) : firstSlice (nullptr), windingMask (p.isUsingNonZeroWinding() ? -1 : 1) { - for (PathFlatteningIterator iter (p); iter.next();) + for (PathFlatteningIterator iter (p, transform); iter.next();) addLine (floatToInt (iter.x1), floatToInt (iter.y1), floatToInt (iter.x2), floatToInt (iter.y2)); } @@ -558,10 +569,10 @@ struct TriangulatedPath::TriangleBlock HeapBlock triangles; }; -TriangulatedPath::TriangulatedPath (const Path& path) +TriangulatedPath::TriangulatedPath (const Path& path, const AffineTransform& transform) { startNewBlock(); - TrapezoidedPath (path).iterate (*this); + TrapezoidedPath (path, transform).iterate (*this); } void TriangulatedPath::draw (const int oversamplingLevel) const @@ -619,4 +630,254 @@ void TriangulatedPath::addTrapezoid (GLfloat y1, GLfloat y2, GLfloat x1, GLfloat currentBlock->numVertices += 12; } +//============================================================================== +OpenGLTextureFromImage::OpenGLTextureFromImage (const Image& image) + : width (image.getWidth()), + height (image.getHeight()) +{ + OpenGLFrameBufferImage* glImage = dynamic_cast (image.getSharedImage()); + + if (glImage != nullptr) + { + textureID = glImage->frameBuffer.getTextureID(); + } + else + { + if (OpenGLTexture::isValidSize (width, height)) + { + texture = new OpenGLTexture(); + texture->load (image); + textureID = texture->getTextureID(); + } + else + { + frameBuffer = new OpenGLFrameBuffer(); + frameBuffer->initialise (image); + textureID = frameBuffer->getTextureID(); + } + } +} + +OpenGLTextureFromImage::~OpenGLTextureFromImage() {} + +//============================================================================== +OpenGLRenderingTarget::OpenGLRenderingTarget() {} +OpenGLRenderingTarget::~OpenGLRenderingTarget() {} + +void OpenGLRenderingTarget::prepareFor2D() +{ + OpenGLHelpers::prepareFor2D (getRenderingTargetWidth(), + getRenderingTargetHeight()); +} + +namespace GLPathRendering +{ + void clipToPath (OpenGLRenderingTarget& target, + const Path& path, const AffineTransform& transform) + { + const int w = target.getRenderingTargetWidth(); + const int h = target.getRenderingTargetHeight(); + + OpenGLFrameBuffer fb; + fb.initialise (w, h); + fb.makeCurrentAndClear(); + fb.createAlphaChannelFromPath (path, transform); + + target.makeCurrentRenderingTarget(); + target.prepareFor2D(); + + glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glBlendFunc (GL_DST_ALPHA, GL_ZERO); + + glColor4f (1.0f, 1.0f, 1.0f, 1.0f); + fb.drawAt (0, 0); + } + + void fillPathWithColour (OpenGLRenderingTarget& target, + const Rectangle& clip, const Path& path, + const AffineTransform& pathTransform, + const Colour& colour) + { + OpenGLFrameBuffer f; + f.initialise (clip.getWidth(), clip.getHeight()); + f.makeCurrentAndClear(); + + f.createAlphaChannelFromPath (path, pathTransform.translated ((float) -clip.getX(), (float) -clip.getY()) + .followedBy (AffineTransform::verticalFlip ((float) clip.getHeight()))); + f.releaseAsRenderingTarget(); + + target.makeCurrentRenderingTarget(); + + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + OpenGLHelpers::setColour (colour); + target.prepareFor2D(); + + f.drawAt ((float) clip.getX(), (float) (target.getRenderingTargetHeight() - clip.getBottom())); + } + + void fillPathWithGradient (OpenGLRenderingTarget& target, + const Rectangle& clip, const Path& path, + const AffineTransform& pathTransform, + const ColourGradient& grad, + const AffineTransform& gradientTransform, + const GLfloat alpha) + { + const int targetHeight = target.getRenderingTargetHeight(); + + OpenGLFrameBuffer f; + f.initialise (clip.getWidth(), clip.getHeight()); + f.makeCurrentAndClear(); + + const AffineTransform correction (AffineTransform::translation ((float) -clip.getX(), (float) -clip.getY()) + .followedBy (AffineTransform::verticalFlip ((float) clip.getHeight()))); + + f.createAlphaChannelFromPath (path, pathTransform.followedBy (correction)); + + f.makeCurrentRenderingTarget(); + f.prepareFor2D(); + + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBlendFunc (GL_DST_ALPHA, GL_ZERO); + + OpenGLHelpers::fillRectWithColourGradient (Rectangle (0, 0, clip.getWidth(), clip.getHeight()), + grad, gradientTransform.followedBy (correction)); + f.releaseAsRenderingTarget(); + target.makeCurrentRenderingTarget(); + + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4f (alpha, alpha, alpha, alpha); + target.prepareFor2D(); + + f.drawAt ((float) clip.getX(), (float) (targetHeight - clip.getBottom())); + } + + void fillPathWithImage (OpenGLRenderingTarget& target, + const Rectangle& clip, const Path& path, + const AffineTransform& transform, + GLuint textureID, GLfloat textureWidth, GLfloat textureHeight, + const AffineTransform& textureTransform, + const bool tiled, + const GLfloat alpha) + { + const int targetHeight = target.getRenderingTargetHeight(); + + OpenGLFrameBuffer f; + f.initialise (clip.getWidth(), clip.getHeight()); + f.makeCurrentRenderingTarget(); + f.prepareFor2D(); + + glDisable (GL_BLEND); + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glColor4f (1.0f, 1.0f, 1.0f, 1.0f); + + const GLfloat clipX = (GLfloat) clip.getX(); + const GLfloat clipY = (GLfloat) clip.getY(); + const GLfloat clipH = (GLfloat) clip.getHeight(); + const GLfloat clipB = (GLfloat) clip.getBottom(); + + const AffineTransform correction (AffineTransform::translation (-clipX, -clipY) + .followedBy (AffineTransform::verticalFlip (clipH))); + + glBindTexture (GL_TEXTURE_2D, textureID); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glEnableClientState (GL_VERTEX_ARRAY); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glDisableClientState (GL_COLOR_ARRAY); + glDisableClientState (GL_NORMAL_ARRAY); + glColor4f (1.0f, 1.0f, 1.0f, 1.0f); + + if (tiled) + { + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + const GLfloat clipW = (GLfloat) clip.getWidth(); + const GLfloat clipR = (GLfloat) clip.getRight(); + + const GLfloat vertices[] = { 0, clipH, clipW, clipH, 0, 0, clipW, 0 }; + GLfloat textureCoords[] = { clipX, clipY, clipR, clipY, clipX, clipB, clipR, clipB }; + + { + const AffineTransform t (textureTransform.inverted().scaled (1.0f / textureWidth, + 1.0f / textureHeight)); + t.transformPoints (textureCoords[0], textureCoords[1], textureCoords[2], textureCoords[3]); + t.transformPoints (textureCoords[4], textureCoords[5], textureCoords[6], textureCoords[7]); + } + + glVertexPointer (2, GL_FLOAT, 0, vertices); + glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); + + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + } + else + { + glClearColor (0, 0, 0, 0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + GLfloat vertices[] = { 0, 0, textureWidth, 0, 0, textureHeight, textureWidth, textureHeight }; + const GLfloat textureCoords[] = { 0, 0, 1.0f, 0, 0, 1.0f, 1.0f, 1.0f }; + + { + const AffineTransform t (textureTransform.followedBy (correction)); + t.transformPoints (vertices[0], vertices[1], vertices[2], vertices[3]); + t.transformPoints (vertices[4], vertices[5], vertices[6], vertices[7]); + } + + glVertexPointer (2, GL_FLOAT, 0, vertices); + glTexCoordPointer (2, GL_FLOAT, 0, textureCoords); + + glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); + } + + glBindTexture (GL_TEXTURE_2D, 0); + + clipToPath (f, path, transform.followedBy (correction)); + + f.releaseAsRenderingTarget(); + target.makeCurrentRenderingTarget(); + + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f (1.0f, 1.0f, 1.0f, alpha); + target.prepareFor2D(); + + f.drawAt (clipX, targetHeight - clipB); + } +} + +void OpenGLRenderingTarget::fillPath (const Rectangle& clip, + const Path& path, const AffineTransform& transform, + const FillType& fill) +{ + if (! fill.isInvisible()) + { + if (fill.isColour()) + { + GLPathRendering::fillPathWithColour (*this, clip, path, transform, fill.colour); + } + else if (fill.isGradient()) + { + GLPathRendering::fillPathWithGradient (*this, clip, path, transform, + *(fill.gradient), fill.transform, + fill.colour.getFloatAlpha()); + } + else if (fill.isTiledImage()) + { + OpenGLTextureFromImage t (fill.image); + + GLPathRendering::fillPathWithImage (*this, clip, path, transform, + t.textureID, (GLfloat) t.width, (GLfloat) t.height, + fill.transform, true, + fill.colour.getFloatAlpha()); + } + } +} + END_JUCE_NAMESPACE diff --git a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h index 202eb7e988..35597fe2d7 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLHelpers.h +++ b/modules/juce_opengl/opengl/juce_OpenGLHelpers.h @@ -26,6 +26,10 @@ #ifndef __JUCE_OPENGLHELPERS_JUCEHEADER__ #define __JUCE_OPENGLHELPERS_JUCEHEADER__ +class OpenGLTexture; +class OpenGLFrameBuffer; + + //============================================================================== /** A set of miscellaneous openGL helper functions. @@ -48,6 +52,8 @@ public: /** This does the same job as gluPerspective(). */ static void setPerspective (double fovy, double aspect, double zNear, double zFar); + static void applyTransform (const AffineTransform& t); + /** Draws a 2D quad with the specified corner points. */ static void drawQuad2D (float x1, float y1, float x2, float y2, @@ -62,6 +68,8 @@ public: float x4, float y4, float z4, const Colour& colour); + static void fillRect (const Rectangle& rect); + /** Fills a rectangle with the specified colour. */ static void fillRectWithColour (const Rectangle& rect, const Colour& colour); @@ -78,7 +86,7 @@ public: class JUCE_API TriangulatedPath { public: - TriangulatedPath (const Path& path); + TriangulatedPath (const Path& path, const AffineTransform& transform); /** Renders the path, using a jittered oversampling method. The oversampling level is the square root of the number of times it @@ -106,4 +114,29 @@ private: }; +//============================================================================== +/** + Used as a local object while rendering, this will create a temporary texture ID + from an image in the quickest way possible. + + If the image is backed by an OpenGL framebuffer, it will use that directly; otherwise, + this object will create a temporary texture or framebuffer and copy the image. +*/ +class JUCE_API OpenGLTextureFromImage +{ +public: + OpenGLTextureFromImage (const Image& image); + ~OpenGLTextureFromImage(); + + GLuint textureID; + const int width, height; + +private: + ScopedPointer texture; + ScopedPointer frameBuffer; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTextureFromImage); +}; + + #endif // __JUCE_OPENGLHELPERS_JUCEHEADER__ diff --git a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp index d931eeb166..562c5fea9d 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLImage.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLImage.cpp @@ -74,7 +74,7 @@ namespace OpenGLImageHelpers { static void read (OpenGLFrameBuffer& frameBuffer, Image::BitmapData& bitmapData, int x, int y) { - frameBuffer.readPixels (bitmapData.data, 0, Rectangle (x, y, bitmapData.width, bitmapData.height)); + frameBuffer.readPixels (bitmapData.data, Rectangle (x, y, bitmapData.width, bitmapData.height)); } }; @@ -86,7 +86,7 @@ namespace OpenGLImageHelpers void write (const void* const data) const noexcept { - frameBuffer.writePixels (data, 0, 4, area); + frameBuffer.writePixels (data, 4, area); } OpenGLFrameBuffer& frameBuffer; diff --git a/modules/juce_opengl/opengl/juce_OpenGLRenderingTarget.h b/modules/juce_opengl/opengl/juce_OpenGLRenderingTarget.h new file mode 100644 index 0000000000..8f57ce43d5 --- /dev/null +++ b/modules/juce_opengl/opengl/juce_OpenGLRenderingTarget.h @@ -0,0 +1,62 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-11 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_OPENGLRENDERINGTARGET_JUCEHEADER__ +#define __JUCE_OPENGLRENDERINGTARGET_JUCEHEADER__ + +//============================================================================== +/** + Base class for OpenGL objects which can be selected as a rendering target. +*/ +class JUCE_API OpenGLRenderingTarget +{ +public: + OpenGLRenderingTarget(); + virtual ~OpenGLRenderingTarget(); + + /** Activates this object as the current OpenGL target. */ + virtual bool makeCurrentRenderingTarget() = 0; + + /** Deactivates this object as the current OpenGL target. */ + virtual void releaseAsRenderingTarget() = 0; + + /** Returns the width in pixels of this target. */ + virtual int getRenderingTargetWidth() const = 0; + /** Returns the height in pixels of this target. */ + virtual int getRenderingTargetHeight() const = 0; + + /** Sets the current matrix for 2D rendering into this object. + @see OpenGLHelpers::prepareFor2D + */ + void prepareFor2D(); + + /** Fills a path with a custom FillType. */ + void fillPath (const Rectangle& clipArea, + const Path& path, const AffineTransform& pathTransform, + const FillType& fill); +}; + + +#endif // __JUCE_OPENGLRENDERINGTARGET_JUCEHEADER__ diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp index 051c6ff46c..f9e2516194 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.cpp @@ -42,16 +42,20 @@ OpenGLTexture::~OpenGLTexture() release(); } +bool OpenGLTexture::isValidSize (int width, int height) +{ + return isPowerOfTwo (width) && isPowerOfTwo (height); +} + void OpenGLTexture::create (const int w, const int h) { + jassert (isValidSize (w, h)); // Perhaps these dimensions must be a power-of-two? + release(); width = w; height = h; - jassert (BitArray (width).countNumberOfSetBits() == 1); // these dimensions must be a power-of-two - jassert (BitArray (height).countNumberOfSetBits() == 1); - glGenTextures (1, &textureID); glBindTexture (GL_TEXTURE_2D, textureID); @@ -67,14 +71,21 @@ void OpenGLTexture::load (const Image& image) { create (image.getWidth(), image.getHeight()); - Image::BitmapData srcData (image, Image::BitmapData::readOnly); + { + Image::BitmapData srcData (image, Image::BitmapData::readOnly); + + glPixelStorei (GL_UNPACK_ALIGNMENT, srcData.pixelFormat); - glPixelStorei (GL_UNPACK_ALIGNMENT, srcData.pixelFormat); - glPixelStorei (GL_UNPACK_ROW_LENGTH, srcData.lineStride); + if (srcData.lineStride == image.getWidth() * srcData.pixelStride) + { + glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, width, height, 0, + srcData.pixelFormat == Image::RGB ? GL_RGB : GL_BGRA_EXT, + GL_UNSIGNED_BYTE, srcData.data); + return; + } + } - glTexImage2D (GL_TEXTURE_2D, 0, internalGLTextureFormat, width, height, 0, - srcData.pixelFormat == Image::RGB ? GL_RGB : GL_BGRA_EXT, - GL_UNSIGNED_BYTE, srcData.data); + load (Image (image.getSharedImage()->clone())); } void OpenGLTexture::load (const PixelARGB* const pixels, const int w, const int h) diff --git a/modules/juce_opengl/opengl/juce_OpenGLTexture.h b/modules/juce_opengl/opengl/juce_OpenGLTexture.h index b369810aa2..cf7b6dc4d8 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLTexture.h +++ b/modules/juce_opengl/opengl/juce_OpenGLTexture.h @@ -26,6 +26,7 @@ #ifndef __JUCE_OPENGLTEXTURE_JUCEHEADER__ #define __JUCE_OPENGLTEXTURE_JUCEHEADER__ +//============================================================================== /** Creates an openGL texture from an Image. */ @@ -66,8 +67,16 @@ public: float x4, float y4, float z4, const Colour& colour) const; + /** Returns the GL texture ID number. */ + GLuint getTextureID() const noexcept { return textureID; } + + /** Returns true if a texture can be created with the given size. + Some systems may require that the sizes are powers-of-two. + */ + static bool isValidSize (int width, int height); + private: - unsigned int textureID; + GLuint textureID; int width, height; void create (int w, int h); @@ -75,4 +84,5 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenGLTexture); }; + #endif // __JUCE_OPENGLTEXTURE_JUCEHEADER__