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.

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