The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

911 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class OpenGLContext::CachedImage : public CachedComponentImage,
  18. public Thread
  19. {
  20. public:
  21. CachedImage (OpenGLContext& c, Component& comp,
  22. const OpenGLPixelFormat& pixFormat, void* contextToShare)
  23. : Thread ("OpenGL Rendering"),
  24. context (c), component (comp),
  25. scale (1.0),
  26. #if JUCE_OPENGL_ES
  27. shadersAvailable (true),
  28. #else
  29. shadersAvailable (false),
  30. #endif
  31. hasInitialised (false),
  32. needsUpdate (1), lastMMLockReleaseTime (0)
  33. {
  34. nativeContext = new NativeContext (component, pixFormat, contextToShare, c.useMultisampling);
  35. if (nativeContext->createdOk())
  36. context.nativeContext = nativeContext;
  37. else
  38. nativeContext = nullptr;
  39. }
  40. ~CachedImage()
  41. {
  42. stop();
  43. }
  44. void start()
  45. {
  46. #if ! JUCE_ANDROID
  47. if (nativeContext != nullptr)
  48. startThread (6);
  49. #endif
  50. }
  51. void stop()
  52. {
  53. #if ! JUCE_ANDROID
  54. stopThread (10000);
  55. #endif
  56. hasInitialised = false;
  57. }
  58. //==============================================================================
  59. void paint (Graphics&) override {}
  60. bool invalidateAll() override
  61. {
  62. validArea.clear();
  63. triggerRepaint();
  64. return false;
  65. }
  66. bool invalidate (const Rectangle<int>& area) override
  67. {
  68. validArea.subtract (area * scale);
  69. triggerRepaint();
  70. return false;
  71. }
  72. void releaseResources() override {}
  73. void triggerRepaint()
  74. {
  75. needsUpdate = 1;
  76. #if JUCE_ANDROID
  77. if (nativeContext != nullptr)
  78. nativeContext->triggerRepaint();
  79. #else
  80. notify();
  81. #endif
  82. }
  83. //==============================================================================
  84. bool ensureFrameBufferSize()
  85. {
  86. const int fbW = cachedImageFrameBuffer.getWidth();
  87. const int fbH = cachedImageFrameBuffer.getHeight();
  88. if (fbW != viewportArea.getWidth() || fbH != viewportArea.getHeight() || ! cachedImageFrameBuffer.isValid())
  89. {
  90. if (! cachedImageFrameBuffer.initialise (context, viewportArea.getWidth(), viewportArea.getHeight()))
  91. return false;
  92. validArea.clear();
  93. JUCE_CHECK_OPENGL_ERROR
  94. }
  95. return true;
  96. }
  97. void clearRegionInFrameBuffer (const RectangleList<int>& list)
  98. {
  99. glClearColor (0, 0, 0, 0);
  100. glEnable (GL_SCISSOR_TEST);
  101. const GLuint previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget();
  102. cachedImageFrameBuffer.makeCurrentRenderingTarget();
  103. const int imageH = cachedImageFrameBuffer.getHeight();
  104. for (const Rectangle<int>* i = list.begin(), * const e = list.end(); i != e; ++i)
  105. {
  106. glScissor (i->getX(), imageH - i->getBottom(), i->getWidth(), i->getHeight());
  107. glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  108. }
  109. glDisable (GL_SCISSOR_TEST);
  110. context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget);
  111. JUCE_CHECK_OPENGL_ERROR
  112. }
  113. bool renderFrame()
  114. {
  115. ScopedPointer<MessageManagerLock> mmLock;
  116. const bool isUpdating = needsUpdate.compareAndSetBool (0, 1);
  117. if (context.renderComponents && isUpdating)
  118. {
  119. // This avoids hogging the message thread when doing intensive rendering.
  120. if (lastMMLockReleaseTime + 1 >= Time::getMillisecondCounter())
  121. wait (2);
  122. mmLock = new MessageManagerLock (this); // need to acquire this before locking the context.
  123. if (! mmLock->lockWasGained())
  124. return false;
  125. }
  126. if (! context.makeActive())
  127. return false;
  128. NativeContext::Locker locker (*nativeContext);
  129. JUCE_CHECK_OPENGL_ERROR
  130. if (context.renderer != nullptr)
  131. {
  132. glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
  133. context.currentRenderScale = scale;
  134. context.renderer->renderOpenGL();
  135. clearGLError();
  136. }
  137. if (context.renderComponents)
  138. {
  139. if (isUpdating)
  140. {
  141. paintComponent();
  142. mmLock = nullptr;
  143. lastMMLockReleaseTime = Time::getMillisecondCounter();
  144. }
  145. glViewport (0, 0, viewportArea.getWidth(), viewportArea.getHeight());
  146. drawComponentBuffer();
  147. }
  148. context.swapBuffers();
  149. return true;
  150. }
  151. void updateViewportSize (bool canTriggerUpdate)
  152. {
  153. if (ComponentPeer* peer = component.getPeer())
  154. {
  155. const double newScale = Desktop::getInstance().getDisplays()
  156. .getDisplayContaining (component.getScreenBounds().getCentre()).scale;
  157. Rectangle<int> newArea (peer->getComponent().getLocalArea (&component, component.getLocalBounds())
  158. .withPosition (0, 0)
  159. * newScale);
  160. if (scale != newScale || viewportArea != newArea)
  161. {
  162. scale = newScale;
  163. viewportArea = newArea;
  164. if (canTriggerUpdate)
  165. invalidateAll();
  166. }
  167. }
  168. }
  169. void paintComponent()
  170. {
  171. // you mustn't set your own cached image object when attaching a GL context!
  172. jassert (get (component) == this);
  173. updateViewportSize (false);
  174. if (! ensureFrameBufferSize())
  175. return;
  176. RectangleList<int> invalid (viewportArea);
  177. invalid.subtract (validArea);
  178. validArea = viewportArea;
  179. if (! invalid.isEmpty())
  180. {
  181. clearRegionInFrameBuffer (invalid);
  182. {
  183. ScopedPointer<LowLevelGraphicsContext> g (createOpenGLGraphicsContext (context, cachedImageFrameBuffer));
  184. g->clipToRectangleList (invalid);
  185. g->addTransform (AffineTransform::scale ((float) scale));
  186. paintOwner (*g);
  187. JUCE_CHECK_OPENGL_ERROR
  188. }
  189. if (! context.isActive())
  190. context.makeActive();
  191. }
  192. JUCE_CHECK_OPENGL_ERROR
  193. }
  194. void drawComponentBuffer()
  195. {
  196. #if ! JUCE_ANDROID
  197. glEnable (GL_TEXTURE_2D);
  198. clearGLError();
  199. #endif
  200. context.extensions.glActiveTexture (GL_TEXTURE0);
  201. glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID());
  202. const Rectangle<int> cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight());
  203. context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight(), false);
  204. glBindTexture (GL_TEXTURE_2D, 0);
  205. JUCE_CHECK_OPENGL_ERROR
  206. }
  207. void paintOwner (LowLevelGraphicsContext& llgc)
  208. {
  209. Graphics g (llgc);
  210. #if JUCE_ENABLE_REPAINT_DEBUGGING
  211. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  212. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  213. #endif
  214. {
  215. g.saveState();
  216. }
  217. #endif
  218. JUCE_TRY
  219. {
  220. component.paintEntireComponent (g, false);
  221. }
  222. JUCE_CATCH_EXCEPTION
  223. #if JUCE_ENABLE_REPAINT_DEBUGGING
  224. #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
  225. if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
  226. #endif
  227. {
  228. // enabling this code will fill all areas that get repainted with a colour overlay, to show
  229. // clearly when things are being repainted.
  230. g.restoreState();
  231. static Random rng;
  232. g.fillAll (Colour ((uint8) rng.nextInt (255),
  233. (uint8) rng.nextInt (255),
  234. (uint8) rng.nextInt (255),
  235. (uint8) 0x50));
  236. }
  237. #endif
  238. }
  239. void handleResize()
  240. {
  241. updateViewportSize (true);
  242. #if JUCE_MAC
  243. if (hasInitialised)
  244. {
  245. [nativeContext->view update];
  246. renderFrame();
  247. }
  248. #endif
  249. }
  250. //==============================================================================
  251. void run() override
  252. {
  253. {
  254. // Allow the message thread to finish setting-up the context before using it..
  255. MessageManagerLock mml (this);
  256. if (! mml.lockWasGained())
  257. return;
  258. }
  259. initialiseOnThread();
  260. hasInitialised = true;
  261. while (! threadShouldExit())
  262. {
  263. if (! renderFrame())
  264. wait (5); // failed to render, so avoid a tight fail-loop.
  265. else if (! context.continuousRepaint)
  266. wait (-1);
  267. }
  268. shutdownOnThread();
  269. }
  270. void initialiseOnThread()
  271. {
  272. // On android, this can get called twice, so drop any previous state..
  273. associatedObjectNames.clear();
  274. associatedObjects.clear();
  275. cachedImageFrameBuffer.release();
  276. context.makeActive();
  277. nativeContext->initialiseOnRenderThread (context);
  278. glViewport (0, 0, component.getWidth(), component.getHeight());
  279. context.extensions.initialise();
  280. nativeContext->setSwapInterval (1);
  281. #if JUCE_USE_OPENGL_SHADERS && ! JUCE_OPENGL_ES
  282. shadersAvailable = OpenGLShaderProgram::getLanguageVersion() > 0;
  283. #endif
  284. if (context.renderer != nullptr)
  285. context.renderer->newOpenGLContextCreated();
  286. }
  287. void shutdownOnThread()
  288. {
  289. if (context.renderer != nullptr)
  290. context.renderer->openGLContextClosing();
  291. cachedImageFrameBuffer.release();
  292. nativeContext->shutdownOnRenderThread();
  293. associatedObjectNames.clear();
  294. associatedObjects.clear();
  295. }
  296. //==============================================================================
  297. static CachedImage* get (Component& c) noexcept
  298. {
  299. return dynamic_cast<CachedImage*> (c.getCachedComponentImage());
  300. }
  301. //==============================================================================
  302. ScopedPointer<NativeContext> nativeContext;
  303. OpenGLContext& context;
  304. Component& component;
  305. OpenGLFrameBuffer cachedImageFrameBuffer;
  306. RectangleList<int> validArea;
  307. Rectangle<int> viewportArea;
  308. double scale;
  309. StringArray associatedObjectNames;
  310. ReferenceCountedArray<ReferenceCountedObject> associatedObjects;
  311. WaitableEvent canPaintNowFlag, finishedPaintingFlag;
  312. bool shadersAvailable, hasInitialised;
  313. Atomic<int> needsUpdate;
  314. uint32 lastMMLockReleaseTime;
  315. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
  316. };
  317. //==============================================================================
  318. #if JUCE_ANDROID
  319. void OpenGLContext::NativeContext::contextCreatedCallback()
  320. {
  321. isInsideGLCallback = true;
  322. if (CachedImage* const c = CachedImage::get (component))
  323. c->initialiseOnThread();
  324. else
  325. jassertfalse;
  326. isInsideGLCallback = false;
  327. }
  328. void OpenGLContext::NativeContext::renderCallback()
  329. {
  330. isInsideGLCallback = true;
  331. if (CachedImage* const c = CachedImage::get (component))
  332. {
  333. if (c->context.continuousRepaint)
  334. c->context.triggerRepaint();
  335. c->renderFrame();
  336. }
  337. isInsideGLCallback = false;
  338. }
  339. #endif
  340. //==============================================================================
  341. class OpenGLContext::Attachment : public ComponentMovementWatcher
  342. {
  343. public:
  344. Attachment (OpenGLContext& c, Component& comp)
  345. : ComponentMovementWatcher (&comp), context (c)
  346. {
  347. if (canBeAttached (comp))
  348. attach();
  349. }
  350. ~Attachment()
  351. {
  352. detach();
  353. }
  354. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  355. {
  356. Component& comp = *getComponent();
  357. if (isAttached (comp) != canBeAttached (comp))
  358. componentVisibilityChanged();
  359. if (comp.getWidth() > 0 && comp.getHeight() > 0
  360. && context.nativeContext != nullptr)
  361. {
  362. if (CachedImage* const c = CachedImage::get (comp))
  363. c->handleResize();
  364. if (ComponentPeer* peer = comp.getTopLevelComponent()->getPeer())
  365. context.nativeContext->updateWindowPosition (peer->getAreaCoveredBy (comp));
  366. }
  367. }
  368. void componentPeerChanged() override
  369. {
  370. detach();
  371. componentVisibilityChanged();
  372. }
  373. void componentVisibilityChanged() override
  374. {
  375. Component& comp = *getComponent();
  376. if (canBeAttached (comp))
  377. {
  378. if (! isAttached (comp))
  379. attach();
  380. }
  381. else
  382. {
  383. detach();
  384. }
  385. }
  386. #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
  387. void componentBeingDeleted (Component& c) override
  388. {
  389. /* You must call detach() or delete your OpenGLContext to remove it
  390. from a component BEFORE deleting the component that it is using!
  391. */
  392. jassertfalse;
  393. ComponentMovementWatcher::componentBeingDeleted (c);
  394. }
  395. #endif
  396. private:
  397. OpenGLContext& context;
  398. static bool canBeAttached (const Component& comp) noexcept
  399. {
  400. return comp.getWidth() > 0 && comp.getHeight() > 0 && isShowingOrMinimised (comp);
  401. }
  402. static bool isShowingOrMinimised (const Component& c)
  403. {
  404. if (! c.isVisible())
  405. return false;
  406. if (Component* p = c.getParentComponent())
  407. return isShowingOrMinimised (*p);
  408. return c.getPeer() != nullptr;
  409. }
  410. static bool isAttached (const Component& comp) noexcept
  411. {
  412. return comp.getCachedComponentImage() != nullptr;
  413. }
  414. void attach()
  415. {
  416. Component& comp = *getComponent();
  417. CachedImage* const newCachedImage = new CachedImage (context, comp,
  418. context.pixelFormat,
  419. context.contextToShareWith);
  420. comp.setCachedComponentImage (newCachedImage);
  421. newCachedImage->start(); // (must wait until this is attached before starting its thread)
  422. newCachedImage->updateViewportSize (true);
  423. }
  424. void detach()
  425. {
  426. Component& comp = *getComponent();
  427. #if JUCE_MAC
  428. [[(NSView*) comp.getWindowHandle() window] disableScreenUpdatesUntilFlush];
  429. #endif
  430. if (CachedImage* const oldCachedImage = CachedImage::get (comp))
  431. oldCachedImage->stop(); // (must stop this before detaching it from the component)
  432. comp.setCachedComponentImage (nullptr);
  433. context.nativeContext = nullptr;
  434. }
  435. };
  436. //==============================================================================
  437. OpenGLContext::OpenGLContext()
  438. : nativeContext (nullptr), renderer (nullptr), currentRenderScale (1.0),
  439. contextToShareWith (nullptr), renderComponents (true),
  440. useMultisampling (false), continuousRepaint (false)
  441. {
  442. }
  443. OpenGLContext::~OpenGLContext()
  444. {
  445. detach();
  446. }
  447. void OpenGLContext::setRenderer (OpenGLRenderer* rendererToUse) noexcept
  448. {
  449. // This method must not be called when the context has already been attached!
  450. // Call it before attaching your context, or use detach() first, before calling this!
  451. jassert (nativeContext == nullptr);
  452. renderer = rendererToUse;
  453. }
  454. void OpenGLContext::setComponentPaintingEnabled (bool shouldPaintComponent) noexcept
  455. {
  456. // This method must not be called when the context has already been attached!
  457. // Call it before attaching your context, or use detach() first, before calling this!
  458. jassert (nativeContext == nullptr);
  459. renderComponents = shouldPaintComponent;
  460. }
  461. void OpenGLContext::setContinuousRepainting (bool shouldContinuouslyRepaint) noexcept
  462. {
  463. continuousRepaint = shouldContinuouslyRepaint;
  464. }
  465. void OpenGLContext::setPixelFormat (const OpenGLPixelFormat& preferredPixelFormat) noexcept
  466. {
  467. // This method must not be called when the context has already been attached!
  468. // Call it before attaching your context, or use detach() first, before calling this!
  469. jassert (nativeContext == nullptr);
  470. pixelFormat = preferredPixelFormat;
  471. }
  472. void OpenGLContext::setNativeSharedContext (void* nativeContextToShareWith) noexcept
  473. {
  474. // This method must not be called when the context has already been attached!
  475. // Call it before attaching your context, or use detach() first, before calling this!
  476. jassert (nativeContext == nullptr);
  477. contextToShareWith = nativeContextToShareWith;
  478. }
  479. void OpenGLContext::setMultisamplingEnabled (bool b) noexcept
  480. {
  481. // This method must not be called when the context has already been attached!
  482. // Call it before attaching your context, or use detach() first, before calling this!
  483. jassert (nativeContext == nullptr);
  484. useMultisampling = b;
  485. }
  486. void OpenGLContext::attachTo (Component& component)
  487. {
  488. component.repaint();
  489. if (getTargetComponent() != &component)
  490. {
  491. detach();
  492. attachment = new Attachment (*this, component);
  493. }
  494. }
  495. void OpenGLContext::detach()
  496. {
  497. attachment = nullptr;
  498. nativeContext = nullptr;
  499. }
  500. bool OpenGLContext::isAttached() const noexcept
  501. {
  502. return nativeContext != nullptr;
  503. }
  504. Component* OpenGLContext::getTargetComponent() const noexcept
  505. {
  506. return attachment != nullptr ? attachment->getComponent() : nullptr;
  507. }
  508. static ThreadLocalValue<OpenGLContext*> currentThreadActiveContext;
  509. OpenGLContext* OpenGLContext::getCurrentContext()
  510. {
  511. return currentThreadActiveContext.get();
  512. }
  513. bool OpenGLContext::makeActive() const noexcept
  514. {
  515. OpenGLContext*& current = currentThreadActiveContext.get();
  516. if (nativeContext != nullptr && nativeContext->makeActive())
  517. {
  518. current = const_cast<OpenGLContext*> (this);
  519. return true;
  520. }
  521. current = nullptr;
  522. return false;
  523. }
  524. bool OpenGLContext::isActive() const noexcept
  525. {
  526. return nativeContext != nullptr && nativeContext->isActive();
  527. }
  528. void OpenGLContext::deactivateCurrentContext()
  529. {
  530. NativeContext::deactivateCurrentContext();
  531. currentThreadActiveContext.get() = nullptr;
  532. }
  533. void OpenGLContext::triggerRepaint()
  534. {
  535. if (CachedImage* const cachedImage = getCachedImage())
  536. cachedImage->triggerRepaint();
  537. }
  538. void OpenGLContext::swapBuffers()
  539. {
  540. if (nativeContext != nullptr)
  541. nativeContext->swapBuffers();
  542. }
  543. unsigned int OpenGLContext::getFrameBufferID() const noexcept
  544. {
  545. return nativeContext != nullptr ? nativeContext->getFrameBufferID() : 0;
  546. }
  547. bool OpenGLContext::setSwapInterval (int numFramesPerSwap)
  548. {
  549. return nativeContext != nullptr && nativeContext->setSwapInterval (numFramesPerSwap);
  550. }
  551. int OpenGLContext::getSwapInterval() const
  552. {
  553. return nativeContext != nullptr ? nativeContext->getSwapInterval() : 0;
  554. }
  555. void* OpenGLContext::getRawContext() const noexcept
  556. {
  557. return nativeContext != nullptr ? nativeContext->getRawContext() : nullptr;
  558. }
  559. OpenGLContext::CachedImage* OpenGLContext::getCachedImage() const noexcept
  560. {
  561. if (Component* const comp = getTargetComponent())
  562. return CachedImage::get (*comp);
  563. return nullptr;
  564. }
  565. bool OpenGLContext::areShadersAvailable() const
  566. {
  567. CachedImage* const c = getCachedImage();
  568. return c != nullptr && c->shadersAvailable;
  569. }
  570. ReferenceCountedObject* OpenGLContext::getAssociatedObject (const char* name) const
  571. {
  572. jassert (name != nullptr);
  573. CachedImage* const c = getCachedImage();
  574. // This method must only be called from an openGL rendering callback.
  575. jassert (c != nullptr && nativeContext != nullptr);
  576. jassert (getCurrentContext() != nullptr);
  577. const int index = c->associatedObjectNames.indexOf (name);
  578. return index >= 0 ? c->associatedObjects.getUnchecked (index) : nullptr;
  579. }
  580. void OpenGLContext::setAssociatedObject (const char* name, ReferenceCountedObject* newObject)
  581. {
  582. jassert (name != nullptr);
  583. if (CachedImage* const c = getCachedImage())
  584. {
  585. // This method must only be called from an openGL rendering callback.
  586. jassert (nativeContext != nullptr);
  587. jassert (getCurrentContext() != nullptr);
  588. const int index = c->associatedObjectNames.indexOf (name);
  589. if (index >= 0)
  590. {
  591. c->associatedObjects.set (index, newObject);
  592. }
  593. else
  594. {
  595. c->associatedObjectNames.add (name);
  596. c->associatedObjects.add (newObject);
  597. }
  598. }
  599. }
  600. void OpenGLContext::copyTexture (const Rectangle<int>& targetClipArea,
  601. const Rectangle<int>& anchorPosAndTextureSize,
  602. const int contextWidth, const int contextHeight,
  603. bool flippedVertically)
  604. {
  605. if (contextWidth <= 0 || contextHeight <= 0)
  606. return;
  607. JUCE_CHECK_OPENGL_ERROR
  608. glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  609. glEnable (GL_BLEND);
  610. #if JUCE_USE_OPENGL_SHADERS
  611. if (areShadersAvailable())
  612. {
  613. struct OverlayShaderProgram : public ReferenceCountedObject
  614. {
  615. OverlayShaderProgram (OpenGLContext& context)
  616. : program (context), builder (program), params (program)
  617. {}
  618. static const OverlayShaderProgram& select (OpenGLContext& context)
  619. {
  620. static const char programValueID[] = "juceGLComponentOverlayShader";
  621. OverlayShaderProgram* program = static_cast <OverlayShaderProgram*> (context.getAssociatedObject (programValueID));
  622. if (program == nullptr)
  623. {
  624. program = new OverlayShaderProgram (context);
  625. context.setAssociatedObject (programValueID, program);
  626. }
  627. program->program.use();
  628. return *program;
  629. }
  630. struct ProgramBuilder
  631. {
  632. ProgramBuilder (OpenGLShaderProgram& prog)
  633. {
  634. prog.addShader ("attribute " JUCE_HIGHP " vec2 position;"
  635. "uniform " JUCE_HIGHP " vec2 screenSize;"
  636. "varying " JUCE_HIGHP " vec2 pixelPos;"
  637. "void main()"
  638. "{"
  639. "pixelPos = position;"
  640. JUCE_HIGHP " vec2 scaled = position / (0.5 * screenSize.xy);"
  641. "gl_Position = vec4 (scaled.x - 1.0, 1.0 - scaled.y, 0, 1.0);"
  642. "}",
  643. GL_VERTEX_SHADER);
  644. prog.addShader ("uniform sampler2D imageTexture;"
  645. "uniform " JUCE_HIGHP " float textureBounds[4];"
  646. "uniform " JUCE_HIGHP " vec2 vOffsetAndScale;"
  647. "varying " JUCE_HIGHP " vec2 pixelPos;"
  648. "void main()"
  649. "{"
  650. JUCE_HIGHP " vec2 texturePos = (pixelPos - vec2 (textureBounds[0], textureBounds[1]))"
  651. "/ vec2 (textureBounds[2], textureBounds[3]);"
  652. "gl_FragColor = texture2D (imageTexture, vec2 (texturePos.x, vOffsetAndScale.x + vOffsetAndScale.y * texturePos.y));"
  653. "}",
  654. GL_FRAGMENT_SHADER);
  655. prog.link();
  656. }
  657. };
  658. struct Params
  659. {
  660. Params (OpenGLShaderProgram& prog)
  661. : positionAttribute (prog, "position"),
  662. screenSize (prog, "screenSize"),
  663. imageTexture (prog, "imageTexture"),
  664. textureBounds (prog, "textureBounds"),
  665. vOffsetAndScale (prog, "vOffsetAndScale")
  666. {}
  667. void set (const float targetWidth, const float targetHeight, const Rectangle<float>& bounds, bool flipVertically) const
  668. {
  669. const GLfloat m[] = { bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() };
  670. textureBounds.set (m, 4);
  671. imageTexture.set (0);
  672. screenSize.set (targetWidth, targetHeight);
  673. vOffsetAndScale.set (flipVertically ? 0.0f : 1.0f,
  674. flipVertically ? 1.0f : -1.0f);
  675. }
  676. OpenGLShaderProgram::Attribute positionAttribute;
  677. OpenGLShaderProgram::Uniform screenSize, imageTexture, textureBounds, vOffsetAndScale;
  678. };
  679. OpenGLShaderProgram program;
  680. ProgramBuilder builder;
  681. Params params;
  682. };
  683. const GLshort left = (GLshort) targetClipArea.getX();
  684. const GLshort top = (GLshort) targetClipArea.getY();
  685. const GLshort right = (GLshort) targetClipArea.getRight();
  686. const GLshort bottom = (GLshort) targetClipArea.getBottom();
  687. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  688. const OverlayShaderProgram& program = OverlayShaderProgram::select (*this);
  689. program.params.set ((float) contextWidth, (float) contextHeight, anchorPosAndTextureSize.toFloat(), flippedVertically);
  690. GLuint vertexBuffer = 0;
  691. extensions.glGenBuffers (1, &vertexBuffer);
  692. extensions.glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
  693. extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
  694. const GLuint index = (GLuint) program.params.positionAttribute.attributeID;
  695. extensions.glVertexAttribPointer (index, 2, GL_SHORT, GL_FALSE, 4, 0);
  696. extensions.glEnableVertexAttribArray (index);
  697. JUCE_CHECK_OPENGL_ERROR
  698. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  699. extensions.glBindBuffer (GL_ARRAY_BUFFER, 0);
  700. extensions.glUseProgram (0);
  701. extensions.glDisableVertexAttribArray (index);
  702. extensions.glDeleteBuffers (1, &vertexBuffer);
  703. }
  704. #if JUCE_USE_OPENGL_FIXED_FUNCTION
  705. else
  706. #endif
  707. #endif
  708. #if JUCE_USE_OPENGL_FIXED_FUNCTION
  709. {
  710. glEnable (GL_SCISSOR_TEST);
  711. glScissor (targetClipArea.getX(), contextHeight - targetClipArea.getBottom(),
  712. targetClipArea.getWidth(), targetClipArea.getHeight());
  713. JUCE_CHECK_OPENGL_ERROR
  714. glColor4f (1.0f, 1.0f, 1.0f, 1.0f);
  715. glDisableClientState (GL_COLOR_ARRAY);
  716. glDisableClientState (GL_NORMAL_ARRAY);
  717. glEnableClientState (GL_VERTEX_ARRAY);
  718. glEnableClientState (GL_TEXTURE_COORD_ARRAY);
  719. OpenGLHelpers::prepareFor2D (contextWidth, contextHeight);
  720. JUCE_CHECK_OPENGL_ERROR
  721. const GLfloat textureCoords[] = { 0, 0, 1.0f, 0, 0, 1.0f, 1.0f, 1.0f };
  722. glTexCoordPointer (2, GL_FLOAT, 0, textureCoords);
  723. const GLshort left = (GLshort) anchorPosAndTextureSize.getX();
  724. const GLshort right = (GLshort) anchorPosAndTextureSize.getRight();
  725. const GLshort top = (GLshort) (contextHeight - anchorPosAndTextureSize.getY());
  726. const GLshort bottom = (GLshort) (contextHeight - anchorPosAndTextureSize.getBottom());
  727. const GLshort vertices[] = { left, bottom, right, bottom, left, top, right, top };
  728. glVertexPointer (2, GL_SHORT, 0, vertices);
  729. glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
  730. glDisable (GL_SCISSOR_TEST);
  731. }
  732. #endif
  733. JUCE_CHECK_OPENGL_ERROR
  734. }