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.

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