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.

781 lines
25KB

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