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.

895 lines
28KB

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