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.

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