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.

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