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.

792 lines
26KB

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