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.

2055 lines
72KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. extern void (*clearOpenGLGlyphCache)(); // declared in juce_graphics
  21. namespace OpenGLRendering
  22. {
  23. struct TextureInfo
  24. {
  25. GLuint textureID;
  26. int imageWidth, imageHeight;
  27. float fullWidthProportion, fullHeightProportion;
  28. };
  29. //==============================================================================
  30. // This list persists in the OpenGLContext, and will re-use cached textures which
  31. // are created from Images.
  32. struct CachedImageList : public ReferenceCountedObject,
  33. private ImagePixelData::Listener
  34. {
  35. CachedImageList (OpenGLContext& c) noexcept
  36. : context (c), maxCacheSize (c.getImageCacheSize()) {}
  37. static CachedImageList* get (OpenGLContext& c)
  38. {
  39. const char cacheValueID[] = "CachedImages";
  40. auto list = static_cast<CachedImageList*> (c.getAssociatedObject (cacheValueID));
  41. if (list == nullptr)
  42. {
  43. list = new CachedImageList (c);
  44. c.setAssociatedObject (cacheValueID, list);
  45. }
  46. return list;
  47. }
  48. TextureInfo getTextureFor (const Image& image)
  49. {
  50. auto pixelData = image.getPixelData();
  51. auto* c = findCachedImage (pixelData);
  52. if (c == nullptr)
  53. {
  54. if (auto fb = OpenGLImageType::getFrameBufferFrom (image))
  55. {
  56. TextureInfo t;
  57. t.textureID = fb->getTextureID();
  58. t.imageWidth = image.getWidth();
  59. t.imageHeight = image.getHeight();
  60. t.fullWidthProportion = 1.0f;
  61. t.fullHeightProportion = 1.0f;
  62. return t;
  63. }
  64. c = images.add (new CachedImage (*this, pixelData));
  65. totalSize += c->imageSize;
  66. while (totalSize > maxCacheSize && images.size() > 1 && totalSize > 0)
  67. removeOldestItem();
  68. }
  69. return c->getTextureInfo();
  70. }
  71. struct CachedImage
  72. {
  73. CachedImage (CachedImageList& list, ImagePixelData* im)
  74. : owner (list), pixelData (im),
  75. lastUsed (Time::getCurrentTime()),
  76. imageSize ((size_t) (im->width * im->height))
  77. {
  78. pixelData->listeners.add (&owner);
  79. }
  80. ~CachedImage()
  81. {
  82. if (pixelData != nullptr)
  83. pixelData->listeners.remove (&owner);
  84. }
  85. TextureInfo getTextureInfo()
  86. {
  87. if (pixelData == nullptr)
  88. return {};
  89. TextureInfo t;
  90. if (textureNeedsReloading)
  91. {
  92. textureNeedsReloading = false;
  93. texture.loadImage (Image (*pixelData));
  94. }
  95. t.textureID = texture.getTextureID();
  96. t.imageWidth = pixelData->width;
  97. t.imageHeight = pixelData->height;
  98. t.fullWidthProportion = (float) t.imageWidth / (float) texture.getWidth();
  99. t.fullHeightProportion = (float) t.imageHeight / (float) texture.getHeight();
  100. lastUsed = Time::getCurrentTime();
  101. return t;
  102. }
  103. CachedImageList& owner;
  104. ImagePixelData* pixelData;
  105. OpenGLTexture texture;
  106. Time lastUsed;
  107. const size_t imageSize;
  108. bool textureNeedsReloading = true;
  109. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImage)
  110. };
  111. using Ptr = ReferenceCountedObjectPtr<CachedImageList>;
  112. private:
  113. OpenGLContext& context;
  114. OwnedArray<CachedImage> images;
  115. size_t totalSize = 0;
  116. const size_t maxCacheSize;
  117. bool canUseContext() const noexcept
  118. {
  119. return OpenGLContext::getCurrentContext() == &context;
  120. }
  121. void imageDataChanged (ImagePixelData* im) override
  122. {
  123. if (auto* c = findCachedImage (im))
  124. c->textureNeedsReloading = true;
  125. }
  126. void imageDataBeingDeleted (ImagePixelData* im) override
  127. {
  128. for (int i = images.size(); --i >= 0;)
  129. {
  130. auto& ci = *images.getUnchecked (i);
  131. if (ci.pixelData == im)
  132. {
  133. if (canUseContext())
  134. {
  135. totalSize -= ci.imageSize;
  136. images.remove (i);
  137. }
  138. else
  139. {
  140. ci.pixelData = nullptr;
  141. }
  142. break;
  143. }
  144. }
  145. }
  146. CachedImage* findCachedImage (ImagePixelData* pixelData) const
  147. {
  148. for (auto& i : images)
  149. if (i->pixelData == pixelData)
  150. return i;
  151. return {};
  152. }
  153. void removeOldestItem()
  154. {
  155. CachedImage* oldest = nullptr;
  156. for (auto& i : images)
  157. if (oldest == nullptr || i->lastUsed < oldest->lastUsed)
  158. oldest = i;
  159. if (oldest != nullptr)
  160. {
  161. totalSize -= oldest->imageSize;
  162. images.removeObject (oldest);
  163. }
  164. }
  165. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedImageList)
  166. };
  167. //==============================================================================
  168. struct Target
  169. {
  170. Target (OpenGLContext& c, GLuint fbID, int width, int height) noexcept
  171. : context (c), frameBufferID (fbID), bounds (width, height)
  172. {}
  173. Target (OpenGLContext& c, OpenGLFrameBuffer& fb, Point<int> origin) noexcept
  174. : context (c), frameBufferID (fb.getFrameBufferID()),
  175. bounds (origin.x, origin.y, fb.getWidth(), fb.getHeight())
  176. {
  177. jassert (frameBufferID != 0); // trying to render into an uninitialised framebuffer object.
  178. }
  179. Target (const Target& other) noexcept
  180. : context (other.context), frameBufferID (other.frameBufferID), bounds (other.bounds)
  181. {}
  182. Target& operator= (const Target& other) noexcept
  183. {
  184. frameBufferID = other.frameBufferID;
  185. bounds = other.bounds;
  186. return *this;
  187. }
  188. void makeActive() const noexcept
  189. {
  190. #if JUCE_WINDOWS
  191. if (context.extensions.glBindFramebuffer != nullptr)
  192. #endif
  193. context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, frameBufferID);
  194. glViewport (0, 0, bounds.getWidth(), bounds.getHeight());
  195. glDisable (GL_DEPTH_TEST);
  196. }
  197. OpenGLContext& context;
  198. GLuint frameBufferID;
  199. Rectangle<int> bounds;
  200. };
  201. //==============================================================================
  202. struct PositionedTexture
  203. {
  204. PositionedTexture (OpenGLTexture& texture, const EdgeTable& et, Rectangle<int> clipRegion)
  205. : clip (clipRegion.getIntersection (et.getMaximumBounds()))
  206. {
  207. if (clip.contains (et.getMaximumBounds()))
  208. {
  209. createMap (texture, et);
  210. }
  211. else
  212. {
  213. EdgeTable et2 (clip);
  214. et2.clipToEdgeTable (et);
  215. createMap (texture, et2);
  216. }
  217. }
  218. PositionedTexture (GLuint texture, Rectangle<int> r, Rectangle<int> clipRegion) noexcept
  219. : textureID (texture), area (r), clip (clipRegion)
  220. {}
  221. GLuint textureID;
  222. Rectangle<int> area, clip;
  223. private:
  224. void createMap (OpenGLTexture& texture, const EdgeTable& et)
  225. {
  226. EdgeTableAlphaMap alphaMap (et);
  227. texture.loadAlpha (alphaMap.data, alphaMap.area.getWidth(), alphaMap.area.getHeight());
  228. textureID = texture.getTextureID();
  229. area = alphaMap.area;
  230. }
  231. struct EdgeTableAlphaMap
  232. {
  233. EdgeTableAlphaMap (const EdgeTable& et)
  234. : area (et.getMaximumBounds().withSize (nextPowerOfTwo (et.getMaximumBounds().getWidth()),
  235. nextPowerOfTwo (et.getMaximumBounds().getHeight())))
  236. {
  237. data.calloc (area.getWidth() * area.getHeight());
  238. et.iterate (*this);
  239. }
  240. inline void setEdgeTableYPos (const int y) noexcept
  241. {
  242. currentLine = data + (area.getBottom() - 1 - y) * area.getWidth() - area.getX();
  243. }
  244. inline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept
  245. {
  246. currentLine[x] = (uint8) alphaLevel;
  247. }
  248. inline void handleEdgeTablePixelFull (const int x) const noexcept
  249. {
  250. currentLine[x] = 255;
  251. }
  252. inline void handleEdgeTableLine (int x, int width, const int alphaLevel) const noexcept
  253. {
  254. memset (currentLine + x, (uint8) alphaLevel, (size_t) width);
  255. }
  256. inline void handleEdgeTableLineFull (int x, int width) const noexcept
  257. {
  258. memset (currentLine + x, 255, (size_t) width);
  259. }
  260. void handleEdgeTableRectangle (int x, int y, int width, int height, int alphaLevel) noexcept
  261. {
  262. while (--height >= 0)
  263. {
  264. setEdgeTableYPos (y++);
  265. handleEdgeTableLine (x, width, alphaLevel);
  266. }
  267. }
  268. void handleEdgeTableRectangleFull (int x, int y, int width, int height) noexcept
  269. {
  270. while (--height >= 0)
  271. {
  272. setEdgeTableYPos (y++);
  273. handleEdgeTableLineFull (x, width);
  274. }
  275. }
  276. HeapBlock<uint8> data;
  277. const Rectangle<int> area;
  278. private:
  279. uint8* currentLine;
  280. JUCE_DECLARE_NON_COPYABLE (EdgeTableAlphaMap)
  281. };
  282. };
  283. //==============================================================================
  284. struct ShaderPrograms : public ReferenceCountedObject
  285. {
  286. ShaderPrograms (OpenGLContext& context)
  287. : solidColourProgram (context),
  288. solidColourMasked (context),
  289. radialGradient (context),
  290. radialGradientMasked (context),
  291. linearGradient1 (context),
  292. linearGradient1Masked (context),
  293. linearGradient2 (context),
  294. linearGradient2Masked (context),
  295. image (context),
  296. imageMasked (context),
  297. tiledImage (context),
  298. tiledImageMasked (context),
  299. copyTexture (context),
  300. maskTexture (context)
  301. {}
  302. using Ptr = ReferenceCountedObjectPtr<ShaderPrograms>;
  303. //==============================================================================
  304. struct ShaderProgramHolder
  305. {
  306. ShaderProgramHolder (OpenGLContext& context, const char* fragmentShader, const char* vertexShader)
  307. : program (context)
  308. {
  309. JUCE_CHECK_OPENGL_ERROR
  310. if (vertexShader == nullptr)
  311. vertexShader = "attribute vec2 position;"
  312. "attribute vec4 colour;"
  313. "uniform vec4 screenBounds;"
  314. "varying " JUCE_MEDIUMP " vec4 frontColour;"
  315. "varying " JUCE_HIGHP " vec2 pixelPos;"
  316. "void main()"
  317. "{"
  318. "frontColour = colour;"
  319. "vec2 adjustedPos = position - screenBounds.xy;"
  320. "pixelPos = adjustedPos;"
  321. "vec2 scaledPos = adjustedPos / screenBounds.zw;"
  322. "gl_Position = vec4 (scaledPos.x - 1.0, 1.0 - scaledPos.y, 0, 1.0);"
  323. "}";
  324. if (program.addVertexShader (OpenGLHelpers::translateVertexShaderToV3 (vertexShader))
  325. && program.addFragmentShader (OpenGLHelpers::translateFragmentShaderToV3 (fragmentShader))
  326. && program.link())
  327. {
  328. JUCE_CHECK_OPENGL_ERROR
  329. }
  330. else
  331. {
  332. lastError = program.getLastError();
  333. }
  334. }
  335. OpenGLShaderProgram program;
  336. String lastError;
  337. };
  338. struct ShaderBase : public ShaderProgramHolder
  339. {
  340. ShaderBase (OpenGLContext& context, const char* fragmentShader, const char* vertexShader = nullptr)
  341. : ShaderProgramHolder (context, fragmentShader, vertexShader),
  342. positionAttribute (program, "position"),
  343. colourAttribute (program, "colour"),
  344. screenBounds (program, "screenBounds")
  345. {}
  346. void set2DBounds (Rectangle<float> bounds)
  347. {
  348. screenBounds.set (bounds.getX(), bounds.getY(), 0.5f * bounds.getWidth(), 0.5f * bounds.getHeight());
  349. }
  350. void bindAttributes()
  351. {
  352. gl::glVertexAttribPointer ((GLuint) positionAttribute.attributeID, 2, GL_SHORT, GL_FALSE, 8, nullptr);
  353. gl::glVertexAttribPointer ((GLuint) colourAttribute.attributeID, 4, GL_UNSIGNED_BYTE, GL_TRUE, 8, (void*) 4);
  354. gl::glEnableVertexAttribArray ((GLuint) positionAttribute.attributeID);
  355. gl::glEnableVertexAttribArray ((GLuint) colourAttribute.attributeID);
  356. }
  357. void unbindAttributes()
  358. {
  359. gl::glDisableVertexAttribArray ((GLuint) positionAttribute.attributeID);
  360. gl::glDisableVertexAttribArray ((GLuint) colourAttribute.attributeID);
  361. }
  362. OpenGLShaderProgram::Attribute positionAttribute, colourAttribute;
  363. OpenGLShaderProgram::Uniform screenBounds;
  364. std::function<void (OpenGLShaderProgram&)> onShaderActivated;
  365. };
  366. struct MaskedShaderParams
  367. {
  368. MaskedShaderParams (OpenGLShaderProgram& program)
  369. : maskTexture (program, "maskTexture"),
  370. maskBounds (program, "maskBounds")
  371. {}
  372. void setBounds (Rectangle<int> area, const Target& target, GLint textureIndex) const
  373. {
  374. maskTexture.set (textureIndex);
  375. maskBounds.set (area.getX() - target.bounds.getX(),
  376. area.getY() - target.bounds.getY(),
  377. area.getWidth(), area.getHeight());
  378. }
  379. OpenGLShaderProgram::Uniform maskTexture, maskBounds;
  380. };
  381. //==============================================================================
  382. #define JUCE_DECLARE_VARYING_COLOUR "varying " JUCE_MEDIUMP " vec4 frontColour;"
  383. #define JUCE_DECLARE_VARYING_PIXELPOS "varying " JUCE_HIGHP " vec2 pixelPos;"
  384. struct SolidColourProgram : public ShaderBase
  385. {
  386. SolidColourProgram (OpenGLContext& context)
  387. : ShaderBase (context, JUCE_DECLARE_VARYING_COLOUR
  388. "void main() { gl_FragColor = frontColour; }")
  389. {}
  390. };
  391. #define JUCE_DECLARE_MASK_UNIFORMS "uniform sampler2D maskTexture;" \
  392. "uniform ivec4 maskBounds;"
  393. #define JUCE_FRAGCOORD_TO_MASK_POS "vec2 ((pixelPos.x - float (maskBounds.x)) / float (maskBounds.z)," \
  394. "1.0 - (pixelPos.y - float (maskBounds.y)) / float (maskBounds.w))"
  395. #define JUCE_GET_MASK_ALPHA "texture2D (maskTexture, " JUCE_FRAGCOORD_TO_MASK_POS ").a"
  396. struct SolidColourMaskedProgram : public ShaderBase
  397. {
  398. SolidColourMaskedProgram (OpenGLContext& context)
  399. : ShaderBase (context,
  400. JUCE_DECLARE_MASK_UNIFORMS JUCE_DECLARE_VARYING_COLOUR JUCE_DECLARE_VARYING_PIXELPOS
  401. "void main() {"
  402. "gl_FragColor = frontColour * " JUCE_GET_MASK_ALPHA ";"
  403. "}"),
  404. maskParams (program)
  405. {}
  406. MaskedShaderParams maskParams;
  407. };
  408. //==============================================================================
  409. struct RadialGradientParams
  410. {
  411. RadialGradientParams (OpenGLShaderProgram& program)
  412. : gradientTexture (program, "gradientTexture"),
  413. matrix (program, "matrix")
  414. {}
  415. void setMatrix (Point<float> p1, Point<float> p2, Point<float> p3)
  416. {
  417. auto t = AffineTransform::fromTargetPoints (p1, Point<float>(),
  418. p2, Point<float> (1.0f, 0.0f),
  419. p3, Point<float> (0.0f, 1.0f));
  420. const GLfloat m[] = { t.mat00, t.mat01, t.mat02, t.mat10, t.mat11, t.mat12 };
  421. matrix.set (m, 6);
  422. }
  423. OpenGLShaderProgram::Uniform gradientTexture, matrix;
  424. };
  425. #define JUCE_DECLARE_MATRIX_UNIFORM "uniform " JUCE_HIGHP " float matrix[6];"
  426. #define JUCE_DECLARE_RADIAL_UNIFORMS "uniform sampler2D gradientTexture;" JUCE_DECLARE_MATRIX_UNIFORM
  427. #define JUCE_MATRIX_TIMES_FRAGCOORD "(mat2 (matrix[0], matrix[3], matrix[1], matrix[4]) * pixelPos" \
  428. " + vec2 (matrix[2], matrix[5]))"
  429. #define JUCE_GET_TEXTURE_COLOUR "(frontColour.a * texture2D (gradientTexture, vec2 (gradientPos, 0.5)))"
  430. struct RadialGradientProgram : public ShaderBase
  431. {
  432. RadialGradientProgram (OpenGLContext& context)
  433. : ShaderBase (context, JUCE_DECLARE_VARYING_PIXELPOS
  434. JUCE_DECLARE_RADIAL_UNIFORMS JUCE_DECLARE_VARYING_COLOUR
  435. "void main()"
  436. "{"
  437. JUCE_MEDIUMP " float gradientPos = length (" JUCE_MATRIX_TIMES_FRAGCOORD ");"
  438. "gl_FragColor = " JUCE_GET_TEXTURE_COLOUR ";"
  439. "}"),
  440. gradientParams (program)
  441. {}
  442. RadialGradientParams gradientParams;
  443. };
  444. struct RadialGradientMaskedProgram : public ShaderBase
  445. {
  446. RadialGradientMaskedProgram (OpenGLContext& context)
  447. : ShaderBase (context, JUCE_DECLARE_VARYING_PIXELPOS
  448. JUCE_DECLARE_RADIAL_UNIFORMS JUCE_DECLARE_VARYING_COLOUR
  449. JUCE_DECLARE_MASK_UNIFORMS
  450. "void main()"
  451. "{"
  452. JUCE_MEDIUMP " float gradientPos = length (" JUCE_MATRIX_TIMES_FRAGCOORD ");"
  453. "gl_FragColor = " JUCE_GET_TEXTURE_COLOUR " * " JUCE_GET_MASK_ALPHA ";"
  454. "}"),
  455. gradientParams (program),
  456. maskParams (program)
  457. {}
  458. RadialGradientParams gradientParams;
  459. MaskedShaderParams maskParams;
  460. };
  461. //==============================================================================
  462. struct LinearGradientParams
  463. {
  464. LinearGradientParams (OpenGLShaderProgram& program)
  465. : gradientTexture (program, "gradientTexture"),
  466. gradientInfo (program, "gradientInfo")
  467. {}
  468. OpenGLShaderProgram::Uniform gradientTexture, gradientInfo;
  469. };
  470. #define JUCE_DECLARE_LINEAR_UNIFORMS "uniform sampler2D gradientTexture;" \
  471. "uniform " JUCE_MEDIUMP " vec4 gradientInfo;" \
  472. JUCE_DECLARE_VARYING_COLOUR JUCE_DECLARE_VARYING_PIXELPOS
  473. #define JUCE_CALC_LINEAR_GRAD_POS1 JUCE_MEDIUMP " float gradientPos = (pixelPos.y - (gradientInfo.y + (gradientInfo.z * (pixelPos.x - gradientInfo.x)))) / gradientInfo.w;"
  474. #define JUCE_CALC_LINEAR_GRAD_POS2 JUCE_MEDIUMP " float gradientPos = (pixelPos.x - (gradientInfo.x + (gradientInfo.z * (pixelPos.y - gradientInfo.y)))) / gradientInfo.w;"
  475. struct LinearGradient1Program : public ShaderBase
  476. {
  477. LinearGradient1Program (OpenGLContext& context)
  478. : ShaderBase (context, JUCE_DECLARE_LINEAR_UNIFORMS // gradientInfo: x = x1, y = y1, z = (y2 - y1) / (x2 - x1), w = length
  479. "void main()"
  480. "{"
  481. JUCE_CALC_LINEAR_GRAD_POS1
  482. "gl_FragColor = " JUCE_GET_TEXTURE_COLOUR ";"
  483. "}"),
  484. gradientParams (program)
  485. {}
  486. LinearGradientParams gradientParams;
  487. };
  488. struct LinearGradient1MaskedProgram : public ShaderBase
  489. {
  490. LinearGradient1MaskedProgram (OpenGLContext& context)
  491. : ShaderBase (context, JUCE_DECLARE_LINEAR_UNIFORMS // gradientInfo: x = x1, y = y1, z = (y2 - y1) / (x2 - x1), w = length
  492. JUCE_DECLARE_MASK_UNIFORMS
  493. "void main()"
  494. "{"
  495. JUCE_CALC_LINEAR_GRAD_POS1
  496. "gl_FragColor = " JUCE_GET_TEXTURE_COLOUR " * " JUCE_GET_MASK_ALPHA ";"
  497. "}"),
  498. gradientParams (program),
  499. maskParams (program)
  500. {}
  501. LinearGradientParams gradientParams;
  502. MaskedShaderParams maskParams;
  503. };
  504. struct LinearGradient2Program : public ShaderBase
  505. {
  506. LinearGradient2Program (OpenGLContext& context)
  507. : ShaderBase (context, JUCE_DECLARE_LINEAR_UNIFORMS // gradientInfo: x = x1, y = y1, z = (x2 - x1) / (y2 - y1), y = y1, w = length
  508. "void main()"
  509. "{"
  510. JUCE_CALC_LINEAR_GRAD_POS2
  511. "gl_FragColor = " JUCE_GET_TEXTURE_COLOUR ";"
  512. "}"),
  513. gradientParams (program)
  514. {}
  515. LinearGradientParams gradientParams;
  516. };
  517. struct LinearGradient2MaskedProgram : public ShaderBase
  518. {
  519. LinearGradient2MaskedProgram (OpenGLContext& context)
  520. : ShaderBase (context, JUCE_DECLARE_LINEAR_UNIFORMS // gradientInfo: x = x1, y = y1, z = (x2 - x1) / (y2 - y1), y = y1, w = length
  521. JUCE_DECLARE_MASK_UNIFORMS
  522. "void main()"
  523. "{"
  524. JUCE_CALC_LINEAR_GRAD_POS2
  525. "gl_FragColor = " JUCE_GET_TEXTURE_COLOUR " * " JUCE_GET_MASK_ALPHA ";"
  526. "}"),
  527. gradientParams (program),
  528. maskParams (program)
  529. {}
  530. LinearGradientParams gradientParams;
  531. MaskedShaderParams maskParams;
  532. };
  533. //==============================================================================
  534. struct ImageParams
  535. {
  536. ImageParams (OpenGLShaderProgram& program)
  537. : imageTexture (program, "imageTexture"),
  538. matrix (program, "matrix"),
  539. imageLimits (program, "imageLimits")
  540. {}
  541. void setMatrix (const AffineTransform& trans, int imageWidth, int imageHeight,
  542. float fullWidthProportion, float fullHeightProportion,
  543. float targetX, float targetY, bool isForTiling) const
  544. {
  545. auto t = trans.translated (-targetX, -targetY)
  546. .inverted().scaled (fullWidthProportion / (float) imageWidth,
  547. fullHeightProportion / (float) imageHeight);
  548. const GLfloat m[] = { t.mat00, t.mat01, t.mat02, t.mat10, t.mat11, t.mat12 };
  549. matrix.set (m, 6);
  550. if (isForTiling)
  551. {
  552. fullWidthProportion -= 0.5f / (float) imageWidth;
  553. fullHeightProportion -= 0.5f / (float) imageHeight;
  554. }
  555. imageLimits.set (fullWidthProportion, fullHeightProportion);
  556. }
  557. void setMatrix (const AffineTransform& trans, const TextureInfo& textureInfo,
  558. float targetX, float targetY, bool isForTiling) const
  559. {
  560. setMatrix (trans,
  561. textureInfo.imageWidth, textureInfo.imageHeight,
  562. textureInfo.fullWidthProportion, textureInfo.fullHeightProportion,
  563. targetX, targetY, isForTiling);
  564. }
  565. OpenGLShaderProgram::Uniform imageTexture, matrix, imageLimits;
  566. };
  567. #define JUCE_DECLARE_IMAGE_UNIFORMS "uniform sampler2D imageTexture;" \
  568. "uniform " JUCE_MEDIUMP " vec2 imageLimits;" \
  569. JUCE_DECLARE_MATRIX_UNIFORM JUCE_DECLARE_VARYING_COLOUR JUCE_DECLARE_VARYING_PIXELPOS
  570. #define JUCE_GET_IMAGE_PIXEL "texture2D (imageTexture, vec2 (texturePos.x, 1.0 - texturePos.y))"
  571. #define JUCE_CLAMP_TEXTURE_COORD JUCE_HIGHP " vec2 texturePos = clamp (" JUCE_MATRIX_TIMES_FRAGCOORD ", vec2 (0, 0), imageLimits);"
  572. #define JUCE_MOD_TEXTURE_COORD JUCE_HIGHP " vec2 texturePos = mod (" JUCE_MATRIX_TIMES_FRAGCOORD ", imageLimits);"
  573. struct ImageProgram : public ShaderBase
  574. {
  575. ImageProgram (OpenGLContext& context)
  576. : ShaderBase (context, JUCE_DECLARE_VARYING_COLOUR
  577. "uniform sampler2D imageTexture;"
  578. "varying " JUCE_HIGHP " vec2 texturePos;"
  579. "void main()"
  580. "{"
  581. "gl_FragColor = frontColour.a * " JUCE_GET_IMAGE_PIXEL ";"
  582. "}",
  583. "uniform " JUCE_MEDIUMP " vec2 imageLimits;"
  584. JUCE_DECLARE_MATRIX_UNIFORM
  585. "attribute vec2 position;"
  586. "attribute vec4 colour;"
  587. "uniform vec4 screenBounds;"
  588. "varying " JUCE_MEDIUMP " vec4 frontColour;"
  589. "varying " JUCE_HIGHP " vec2 texturePos;"
  590. "void main()"
  591. "{"
  592. "frontColour = colour;"
  593. "vec2 adjustedPos = position - screenBounds.xy;"
  594. "vec2 pixelPos = adjustedPos;"
  595. "texturePos = clamp (" JUCE_MATRIX_TIMES_FRAGCOORD ", vec2 (0, 0), imageLimits);"
  596. "vec2 scaledPos = adjustedPos / screenBounds.zw;"
  597. "gl_Position = vec4 (scaledPos.x - 1.0, 1.0 - scaledPos.y, 0, 1.0);"
  598. "}"),
  599. imageParams (program)
  600. {}
  601. ImageParams imageParams;
  602. };
  603. struct ImageMaskedProgram : public ShaderBase
  604. {
  605. ImageMaskedProgram (OpenGLContext& context)
  606. : ShaderBase (context, JUCE_DECLARE_IMAGE_UNIFORMS JUCE_DECLARE_MASK_UNIFORMS
  607. "void main()"
  608. "{"
  609. JUCE_CLAMP_TEXTURE_COORD
  610. "gl_FragColor = frontColour.a * " JUCE_GET_IMAGE_PIXEL " * " JUCE_GET_MASK_ALPHA ";"
  611. "}"),
  612. imageParams (program),
  613. maskParams (program)
  614. {}
  615. ImageParams imageParams;
  616. MaskedShaderParams maskParams;
  617. };
  618. struct TiledImageProgram : public ShaderBase
  619. {
  620. TiledImageProgram (OpenGLContext& context)
  621. : ShaderBase (context, JUCE_DECLARE_IMAGE_UNIFORMS
  622. "void main()"
  623. "{"
  624. JUCE_MOD_TEXTURE_COORD
  625. "gl_FragColor = frontColour.a * " JUCE_GET_IMAGE_PIXEL ";"
  626. "}"),
  627. imageParams (program)
  628. {}
  629. ImageParams imageParams;
  630. };
  631. struct TiledImageMaskedProgram : public ShaderBase
  632. {
  633. TiledImageMaskedProgram (OpenGLContext& context)
  634. : ShaderBase (context, JUCE_DECLARE_IMAGE_UNIFORMS JUCE_DECLARE_MASK_UNIFORMS
  635. "void main()"
  636. "{"
  637. JUCE_MOD_TEXTURE_COORD
  638. "gl_FragColor = frontColour.a * " JUCE_GET_IMAGE_PIXEL " * " JUCE_GET_MASK_ALPHA ";"
  639. "}"),
  640. imageParams (program),
  641. maskParams (program)
  642. {}
  643. ImageParams imageParams;
  644. MaskedShaderParams maskParams;
  645. };
  646. struct CopyTextureProgram : public ShaderBase
  647. {
  648. CopyTextureProgram (OpenGLContext& context)
  649. : ShaderBase (context, JUCE_DECLARE_IMAGE_UNIFORMS
  650. "void main()"
  651. "{"
  652. JUCE_MOD_TEXTURE_COORD
  653. "gl_FragColor = frontColour.a * " JUCE_GET_IMAGE_PIXEL ";"
  654. "}"),
  655. imageParams (program)
  656. {}
  657. ImageParams imageParams;
  658. };
  659. struct MaskTextureProgram : public ShaderBase
  660. {
  661. MaskTextureProgram (OpenGLContext& context)
  662. : ShaderBase (context, JUCE_DECLARE_IMAGE_UNIFORMS
  663. "void main()"
  664. "{"
  665. JUCE_HIGHP " vec2 texturePos = " JUCE_MATRIX_TIMES_FRAGCOORD ";"
  666. JUCE_HIGHP " float roundingError = 0.00001;"
  667. "if (texturePos.x >= -roundingError"
  668. "&& texturePos.y >= -roundingError"
  669. "&& texturePos.x <= imageLimits.x + roundingError"
  670. "&& texturePos.y <= imageLimits.y + roundingError)"
  671. "gl_FragColor = frontColour * " JUCE_GET_IMAGE_PIXEL ".a;"
  672. "else "
  673. "gl_FragColor = vec4 (0, 0, 0, 0);"
  674. "}"),
  675. imageParams (program)
  676. {}
  677. ImageParams imageParams;
  678. };
  679. SolidColourProgram solidColourProgram;
  680. SolidColourMaskedProgram solidColourMasked;
  681. RadialGradientProgram radialGradient;
  682. RadialGradientMaskedProgram radialGradientMasked;
  683. LinearGradient1Program linearGradient1;
  684. LinearGradient1MaskedProgram linearGradient1Masked;
  685. LinearGradient2Program linearGradient2;
  686. LinearGradient2MaskedProgram linearGradient2Masked;
  687. ImageProgram image;
  688. ImageMaskedProgram imageMasked;
  689. TiledImageProgram tiledImage;
  690. TiledImageMaskedProgram tiledImageMasked;
  691. CopyTextureProgram copyTexture;
  692. MaskTextureProgram maskTexture;
  693. };
  694. //==============================================================================
  695. struct TraitsVAO
  696. {
  697. static bool isCoreProfile()
  698. {
  699. #if JUCE_OPENGL_ES
  700. return true;
  701. #else
  702. clearGLError();
  703. GLint mask = 0;
  704. glGetIntegerv (GL_CONTEXT_PROFILE_MASK, &mask);
  705. // The context isn't aware of the profile mask, so it pre-dates the core profile
  706. if (glGetError() == GL_INVALID_ENUM)
  707. return false;
  708. // Also assumes a compatibility profile if the mask is completely empty for some reason
  709. return (mask & (GLint) GL_CONTEXT_CORE_PROFILE_BIT) != 0;
  710. #endif
  711. }
  712. /* Returns true if the context requires a non-zero vertex array object (VAO) to be bound.
  713. If the context is a compatibility context, we can just pretend that VAOs don't exist,
  714. and use the default VAO all the time instead. This provides a more consistent experience
  715. in user code, which might make calls (like glVertexPointer()) that only work when VAO 0 is
  716. bound in OpenGL 3.2+.
  717. */
  718. static bool shouldUseCustomVAO()
  719. {
  720. #if JUCE_OPENGL_ES
  721. return false;
  722. #else
  723. return isCoreProfile();
  724. #endif
  725. }
  726. static constexpr auto value = GL_VERTEX_ARRAY_BINDING;
  727. static constexpr auto& gen = glGenVertexArrays;
  728. static constexpr auto& del = glDeleteVertexArrays;
  729. template <typename T>
  730. static void bind (T x) { gl::glBindVertexArray (static_cast<GLuint> (x)); }
  731. static constexpr auto predicate = shouldUseCustomVAO;
  732. };
  733. struct TraitsArrayBuffer
  734. {
  735. static constexpr auto value = GL_ARRAY_BUFFER_BINDING;
  736. static constexpr auto& gen = glGenBuffers;
  737. static constexpr auto& del = glDeleteBuffers;
  738. template <typename T>
  739. static void bind (T x) { gl::glBindBuffer (GL_ARRAY_BUFFER, static_cast<GLuint> (x)); }
  740. static bool predicate() { return true; }
  741. };
  742. struct TraitsElementArrayBuffer
  743. {
  744. static constexpr auto value = GL_ELEMENT_ARRAY_BUFFER_BINDING;
  745. static constexpr auto& gen = glGenBuffers;
  746. static constexpr auto& del = glDeleteBuffers;
  747. template <typename T>
  748. static void bind (T x) { gl::glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, static_cast<GLuint> (x)); }
  749. static bool predicate() { return true; }
  750. };
  751. template <typename Traits>
  752. class SavedBinding
  753. {
  754. public:
  755. SavedBinding() = default;
  756. ~SavedBinding()
  757. {
  758. if (! Traits::predicate())
  759. return;
  760. Traits::bind (values.previous);
  761. Traits::del (1, &values.current);
  762. }
  763. void bind() const { Traits::bind (values.current); }
  764. JUCE_DECLARE_NON_COPYABLE (SavedBinding)
  765. JUCE_DECLARE_NON_MOVEABLE (SavedBinding)
  766. private:
  767. struct Values
  768. {
  769. GLint previous{};
  770. GLuint current{};
  771. };
  772. Values values = []
  773. {
  774. if (! Traits::predicate())
  775. return Values{};
  776. GLint previous{};
  777. glGetIntegerv (Traits::value, &previous);
  778. GLuint current{};
  779. Traits::gen (1, &current);
  780. Traits::bind (current);
  781. return Values { previous, current };
  782. }();
  783. };
  784. //==============================================================================
  785. struct StateHelpers
  786. {
  787. struct BlendingMode
  788. {
  789. BlendingMode() noexcept {}
  790. void resync() noexcept
  791. {
  792. glDisable (GL_BLEND);
  793. srcFunction = dstFunction = 0;
  794. }
  795. template <typename QuadQueueType>
  796. void setPremultipliedBlendingMode (QuadQueueType& quadQueue) noexcept
  797. {
  798. setBlendFunc (quadQueue, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  799. }
  800. template <typename QuadQueueType>
  801. void setBlendFunc (QuadQueueType& quadQueue, GLenum src, GLenum dst)
  802. {
  803. if (! blendingEnabled)
  804. {
  805. quadQueue.flush();
  806. blendingEnabled = true;
  807. glEnable (GL_BLEND);
  808. }
  809. if (srcFunction != src || dstFunction != dst)
  810. {
  811. quadQueue.flush();
  812. srcFunction = src;
  813. dstFunction = dst;
  814. glBlendFunc (src, dst);
  815. }
  816. }
  817. template <typename QuadQueueType>
  818. void disableBlend (QuadQueueType& quadQueue) noexcept
  819. {
  820. if (blendingEnabled)
  821. {
  822. quadQueue.flush();
  823. blendingEnabled = false;
  824. glDisable (GL_BLEND);
  825. }
  826. }
  827. template <typename QuadQueueType>
  828. void setBlendMode (QuadQueueType& quadQueue, bool replaceExistingContents) noexcept
  829. {
  830. if (replaceExistingContents)
  831. disableBlend (quadQueue);
  832. else
  833. setPremultipliedBlendingMode (quadQueue);
  834. }
  835. private:
  836. bool blendingEnabled = false;
  837. GLenum srcFunction = 0, dstFunction = 0;
  838. };
  839. //==============================================================================
  840. template <typename QuadQueueType>
  841. struct EdgeTableRenderer
  842. {
  843. EdgeTableRenderer (QuadQueueType& q, PixelARGB c) noexcept
  844. : quadQueue (q), colour (c)
  845. {}
  846. void setEdgeTableYPos (int y) noexcept
  847. {
  848. currentY = y;
  849. }
  850. void handleEdgeTablePixel (int x, int alphaLevel) noexcept
  851. {
  852. auto c = colour;
  853. c.multiplyAlpha (alphaLevel);
  854. quadQueue.add (x, currentY, 1, 1, c);
  855. }
  856. void handleEdgeTablePixelFull (int x) noexcept
  857. {
  858. quadQueue.add (x, currentY, 1, 1, colour);
  859. }
  860. void handleEdgeTableLine (int x, int width, int alphaLevel) noexcept
  861. {
  862. auto c = colour;
  863. c.multiplyAlpha (alphaLevel);
  864. quadQueue.add (x, currentY, width, 1, c);
  865. }
  866. void handleEdgeTableLineFull (int x, int width) noexcept
  867. {
  868. quadQueue.add (x, currentY, width, 1, colour);
  869. }
  870. void handleEdgeTableRectangle (int x, int y, int width, int height, int alphaLevel) noexcept
  871. {
  872. auto c = colour;
  873. c.multiplyAlpha (alphaLevel);
  874. quadQueue.add (x, y, width, height, c);
  875. }
  876. void handleEdgeTableRectangleFull (int x, int y, int width, int height) noexcept
  877. {
  878. quadQueue.add (x, y, width, height, colour);
  879. }
  880. private:
  881. QuadQueueType& quadQueue;
  882. const PixelARGB colour;
  883. int currentY;
  884. JUCE_DECLARE_NON_COPYABLE (EdgeTableRenderer)
  885. };
  886. template <typename QuadQueueType>
  887. struct FloatRectangleRenderer
  888. {
  889. FloatRectangleRenderer (QuadQueueType& q, PixelARGB c) noexcept
  890. : quadQueue (q), colour (c)
  891. {}
  892. void operator() (int x, int y, int w, int h, int alpha) noexcept
  893. {
  894. if (w > 0 && h > 0)
  895. {
  896. PixelARGB c (colour);
  897. c.multiplyAlpha (alpha);
  898. quadQueue.add (x, y, w, h, c);
  899. }
  900. }
  901. private:
  902. QuadQueueType& quadQueue;
  903. const PixelARGB colour;
  904. JUCE_DECLARE_NON_COPYABLE (FloatRectangleRenderer)
  905. };
  906. //==============================================================================
  907. struct ActiveTextures
  908. {
  909. explicit ActiveTextures (const OpenGLContext& c) noexcept
  910. : context (c)
  911. {
  912. }
  913. void clear() noexcept
  914. {
  915. zeromem (currentTextureID, sizeof (currentTextureID));
  916. }
  917. template <typename QuadQueueType>
  918. void setTexturesEnabled (QuadQueueType& quadQueue, int textureIndexMask) noexcept
  919. {
  920. if (texturesEnabled != textureIndexMask)
  921. {
  922. quadQueue.flush();
  923. for (int i = numTextures; --i >= 0;)
  924. {
  925. if ((texturesEnabled & (1 << i)) != (textureIndexMask & (1 << i)))
  926. {
  927. setActiveTexture (i);
  928. JUCE_CHECK_OPENGL_ERROR
  929. const auto thisTextureEnabled = (textureIndexMask & (1 << i)) != 0;
  930. if (! thisTextureEnabled)
  931. currentTextureID[i] = 0;
  932. #if ! JUCE_ANDROID
  933. if (needsToEnableTexture)
  934. {
  935. if (thisTextureEnabled)
  936. glEnable (GL_TEXTURE_2D);
  937. else
  938. glDisable (GL_TEXTURE_2D);
  939. JUCE_CHECK_OPENGL_ERROR
  940. }
  941. #endif
  942. }
  943. }
  944. texturesEnabled = textureIndexMask;
  945. }
  946. }
  947. template <typename QuadQueueType>
  948. void disableTextures (QuadQueueType& quadQueue) noexcept
  949. {
  950. setTexturesEnabled (quadQueue, 0);
  951. }
  952. template <typename QuadQueueType>
  953. void setSingleTextureMode (QuadQueueType& quadQueue) noexcept
  954. {
  955. setTexturesEnabled (quadQueue, 1);
  956. setActiveTexture (0);
  957. }
  958. template <typename QuadQueueType>
  959. void setTwoTextureMode (QuadQueueType& quadQueue, GLuint texture1, GLuint texture2)
  960. {
  961. JUCE_CHECK_OPENGL_ERROR
  962. setTexturesEnabled (quadQueue, 3);
  963. if (currentActiveTexture == 0)
  964. {
  965. bindTexture (texture1);
  966. setActiveTexture (1);
  967. bindTexture (texture2);
  968. }
  969. else
  970. {
  971. setActiveTexture (1);
  972. bindTexture (texture2);
  973. setActiveTexture (0);
  974. bindTexture (texture1);
  975. }
  976. JUCE_CHECK_OPENGL_ERROR
  977. }
  978. void setActiveTexture (int index) noexcept
  979. {
  980. if (currentActiveTexture != index)
  981. {
  982. currentActiveTexture = index;
  983. context.extensions.glActiveTexture (GL_TEXTURE0 + (GLenum) index);
  984. JUCE_CHECK_OPENGL_ERROR
  985. }
  986. }
  987. void bindTexture (GLuint textureID) noexcept
  988. {
  989. if (currentActiveTexture < 0 || numTextures <= currentActiveTexture)
  990. {
  991. jassertfalse;
  992. return;
  993. }
  994. if (currentTextureID[currentActiveTexture] != textureID)
  995. {
  996. currentTextureID[currentActiveTexture] = textureID;
  997. glBindTexture (GL_TEXTURE_2D, textureID);
  998. JUCE_CHECK_OPENGL_ERROR
  999. }
  1000. else
  1001. {
  1002. #if JUCE_DEBUG
  1003. GLint t = 0;
  1004. glGetIntegerv (GL_TEXTURE_BINDING_2D, &t);
  1005. jassert (t == (GLint) textureID);
  1006. #endif
  1007. }
  1008. }
  1009. private:
  1010. static constexpr auto numTextures = 3;
  1011. GLuint currentTextureID[numTextures];
  1012. int texturesEnabled = 0, currentActiveTexture = -1;
  1013. const OpenGLContext& context;
  1014. const bool needsToEnableTexture = ! context.isCoreProfile();
  1015. ActiveTextures& operator= (const ActiveTextures&);
  1016. };
  1017. //==============================================================================
  1018. struct TextureCache
  1019. {
  1020. TextureCache() noexcept {}
  1021. OpenGLTexture* getTexture (ActiveTextures& activeTextures, int w, int h)
  1022. {
  1023. if (textures.size() < numTexturesToCache)
  1024. {
  1025. activeTextures.clear();
  1026. return new OpenGLTexture();
  1027. }
  1028. for (int i = 0; i < numTexturesToCache - 2; ++i)
  1029. {
  1030. auto* t = textures.getUnchecked (i);
  1031. if (t->getWidth() == w && t->getHeight() == h)
  1032. return textures.removeAndReturn (i);
  1033. }
  1034. return textures.removeAndReturn (0);
  1035. }
  1036. void resetGradient() noexcept
  1037. {
  1038. gradientNeedsRefresh = true;
  1039. }
  1040. void bindTextureForGradient (ActiveTextures& activeTextures, const ColourGradient& gradient)
  1041. {
  1042. if (gradientNeedsRefresh)
  1043. {
  1044. gradientNeedsRefresh = false;
  1045. if (gradientTextures.size() < numGradientTexturesToCache)
  1046. {
  1047. activeGradientIndex = gradientTextures.size();
  1048. activeTextures.clear();
  1049. gradientTextures.add (new OpenGLTexture());
  1050. }
  1051. else
  1052. {
  1053. activeGradientIndex = (activeGradientIndex + 1) % numGradientTexturesToCache;
  1054. }
  1055. JUCE_CHECK_OPENGL_ERROR;
  1056. PixelARGB lookup[gradientTextureSize];
  1057. gradient.createLookupTable (lookup);
  1058. gradientTextures.getUnchecked (activeGradientIndex)->loadARGB (lookup, gradientTextureSize, 1);
  1059. }
  1060. activeTextures.bindTexture (gradientTextures.getUnchecked (activeGradientIndex)->getTextureID());
  1061. }
  1062. enum { gradientTextureSize = 256 };
  1063. private:
  1064. enum { numTexturesToCache = 8, numGradientTexturesToCache = 10 };
  1065. OwnedArray<OpenGLTexture> textures, gradientTextures;
  1066. int activeGradientIndex = 0;
  1067. bool gradientNeedsRefresh = true;
  1068. };
  1069. //==============================================================================
  1070. struct ShaderQuadQueue
  1071. {
  1072. ShaderQuadQueue (const OpenGLContext& c) noexcept : context (c)
  1073. {}
  1074. ~ShaderQuadQueue() noexcept
  1075. {
  1076. static_assert (sizeof (VertexInfo) == 8, "Sanity check VertexInfo size");
  1077. }
  1078. void initialise() noexcept
  1079. {
  1080. JUCE_CHECK_OPENGL_ERROR
  1081. #if JUCE_ANDROID || JUCE_IOS
  1082. int numQuads = maxNumQuads;
  1083. #else
  1084. GLint maxIndices = 0;
  1085. glGetIntegerv (GL_MAX_ELEMENTS_INDICES, &maxIndices);
  1086. auto numQuads = jmin ((int) maxNumQuads, (int) maxIndices / 6);
  1087. maxVertices = numQuads * 4 - 4;
  1088. #endif
  1089. for (int i = 0, v = 0; i < numQuads * 6; i += 6, v += 4)
  1090. {
  1091. indexData[i] = (GLushort) v;
  1092. indexData[i + 1] = indexData[i + 3] = (GLushort) (v + 1);
  1093. indexData[i + 2] = indexData[i + 4] = (GLushort) (v + 2);
  1094. indexData[i + 5] = (GLushort) (v + 3);
  1095. }
  1096. savedElementArrayBuffer.bind();
  1097. context.extensions.glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indexData), indexData, GL_STATIC_DRAW);
  1098. savedArrayBuffer.bind();
  1099. context.extensions.glBufferData (GL_ARRAY_BUFFER, sizeof (vertexData), vertexData, GL_STREAM_DRAW);
  1100. JUCE_CHECK_OPENGL_ERROR
  1101. }
  1102. void add (int x, int y, int w, int h, PixelARGB colour) noexcept
  1103. {
  1104. jassert (w > 0 && h > 0);
  1105. auto* v = vertexData + numVertices;
  1106. v[0].x = v[2].x = (GLshort) x;
  1107. v[0].y = v[1].y = (GLshort) y;
  1108. v[1].x = v[3].x = (GLshort) (x + w);
  1109. v[2].y = v[3].y = (GLshort) (y + h);
  1110. #if JUCE_BIG_ENDIAN
  1111. auto rgba = (GLuint) ((colour.getRed() << 24) | (colour.getGreen() << 16)
  1112. | (colour.getBlue() << 8) | colour.getAlpha());
  1113. #else
  1114. auto rgba = (GLuint) ((colour.getAlpha() << 24) | (colour.getBlue() << 16)
  1115. | (colour.getGreen() << 8) | colour.getRed());
  1116. #endif
  1117. v[0].colour = rgba;
  1118. v[1].colour = rgba;
  1119. v[2].colour = rgba;
  1120. v[3].colour = rgba;
  1121. numVertices += 4;
  1122. if (numVertices > maxVertices)
  1123. draw();
  1124. }
  1125. void add (Rectangle<int> r, PixelARGB colour) noexcept
  1126. {
  1127. add (r.getX(), r.getY(), r.getWidth(), r.getHeight(), colour);
  1128. }
  1129. void add (Rectangle<float> r, PixelARGB colour) noexcept
  1130. {
  1131. FloatRectangleRenderer<ShaderQuadQueue> frr (*this, colour);
  1132. RenderingHelpers::FloatRectangleRasterisingInfo (r).iterate (frr);
  1133. }
  1134. void add (const RectangleList<int>& list, PixelARGB colour) noexcept
  1135. {
  1136. for (auto& i : list)
  1137. add (i, colour);
  1138. }
  1139. void add (const RectangleList<int>& list, Rectangle<int> clip, PixelARGB colour) noexcept
  1140. {
  1141. for (auto& i : list)
  1142. {
  1143. auto r = i.getIntersection (clip);
  1144. if (! r.isEmpty())
  1145. add (r, colour);
  1146. }
  1147. }
  1148. template <typename IteratorType>
  1149. void add (const IteratorType& et, PixelARGB colour)
  1150. {
  1151. EdgeTableRenderer<ShaderQuadQueue> etr (*this, colour);
  1152. et.iterate (etr);
  1153. }
  1154. void flush() noexcept
  1155. {
  1156. if (numVertices > 0)
  1157. draw();
  1158. }
  1159. private:
  1160. struct VertexInfo
  1161. {
  1162. GLshort x, y;
  1163. GLuint colour;
  1164. };
  1165. enum { maxNumQuads = 256 };
  1166. SavedBinding<TraitsArrayBuffer> savedArrayBuffer;
  1167. SavedBinding<TraitsElementArrayBuffer> savedElementArrayBuffer;
  1168. VertexInfo vertexData[maxNumQuads * 4];
  1169. GLushort indexData[maxNumQuads * 6];
  1170. const OpenGLContext& context;
  1171. int numVertices = 0;
  1172. #if JUCE_ANDROID || JUCE_IOS
  1173. enum { maxVertices = maxNumQuads * 4 - 4 };
  1174. #else
  1175. int maxVertices = 0;
  1176. #endif
  1177. void draw() noexcept
  1178. {
  1179. context.extensions.glBufferSubData (GL_ARRAY_BUFFER, 0, (GLsizeiptr) ((size_t) numVertices * sizeof (VertexInfo)), vertexData);
  1180. // NB: If you get a random crash in here and are running in a Parallels VM, it seems to be a bug in
  1181. // their driver.. Can't find a workaround unfortunately.
  1182. glDrawElements (GL_TRIANGLES, (numVertices * 3) / 2, GL_UNSIGNED_SHORT, nullptr);
  1183. JUCE_CHECK_OPENGL_ERROR
  1184. numVertices = 0;
  1185. }
  1186. JUCE_DECLARE_NON_COPYABLE (ShaderQuadQueue)
  1187. };
  1188. //==============================================================================
  1189. struct CurrentShader
  1190. {
  1191. CurrentShader (OpenGLContext& c) noexcept : context (c)
  1192. {
  1193. auto programValueID = "GraphicsContextPrograms";
  1194. programs = static_cast<ShaderPrograms*> (context.getAssociatedObject (programValueID));
  1195. if (programs == nullptr)
  1196. {
  1197. programs = new ShaderPrograms (context);
  1198. context.setAssociatedObject (programValueID, programs.get());
  1199. }
  1200. }
  1201. ~CurrentShader()
  1202. {
  1203. jassert (activeShader == nullptr);
  1204. }
  1205. void setShader (Rectangle<int> bounds, ShaderQuadQueue& quadQueue, ShaderPrograms::ShaderBase& shader)
  1206. {
  1207. if (activeShader != &shader)
  1208. {
  1209. clearShader (quadQueue);
  1210. activeShader = &shader;
  1211. shader.program.use();
  1212. shader.bindAttributes();
  1213. if (shader.onShaderActivated)
  1214. shader.onShaderActivated (shader.program);
  1215. currentBounds = bounds;
  1216. shader.set2DBounds (bounds.toFloat());
  1217. JUCE_CHECK_OPENGL_ERROR
  1218. }
  1219. else if (bounds != currentBounds)
  1220. {
  1221. currentBounds = bounds;
  1222. shader.set2DBounds (bounds.toFloat());
  1223. }
  1224. }
  1225. void setShader (Target& target, ShaderQuadQueue& quadQueue, ShaderPrograms::ShaderBase& shader)
  1226. {
  1227. setShader (target.bounds, quadQueue, shader);
  1228. }
  1229. void clearShader (ShaderQuadQueue& quadQueue)
  1230. {
  1231. if (activeShader != nullptr)
  1232. {
  1233. quadQueue.flush();
  1234. activeShader->unbindAttributes();
  1235. activeShader = nullptr;
  1236. context.extensions.glUseProgram (0);
  1237. }
  1238. }
  1239. OpenGLContext& context;
  1240. ShaderPrograms::Ptr programs;
  1241. private:
  1242. ShaderPrograms::ShaderBase* activeShader = nullptr;
  1243. Rectangle<int> currentBounds;
  1244. CurrentShader& operator= (const CurrentShader&);
  1245. };
  1246. };
  1247. //==============================================================================
  1248. struct GLState
  1249. {
  1250. GLState (const Target& t) noexcept
  1251. : target (t),
  1252. activeTextures (t.context),
  1253. currentShader (t.context),
  1254. shaderQuadQueue (t.context),
  1255. previousFrameBufferTarget (OpenGLFrameBuffer::getCurrentFrameBufferTarget())
  1256. {
  1257. // This object can only be created and used when the current thread has an active OpenGL context.
  1258. jassert (OpenGLHelpers::isContextActive());
  1259. JUCE_CHECK_OPENGL_ERROR
  1260. target.makeActive();
  1261. blendMode.resync();
  1262. JUCE_CHECK_OPENGL_ERROR
  1263. activeTextures.clear();
  1264. shaderQuadQueue.initialise();
  1265. cachedImageList = CachedImageList::get (t.context);
  1266. JUCE_CHECK_OPENGL_ERROR
  1267. }
  1268. ~GLState()
  1269. {
  1270. flush();
  1271. target.context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget);
  1272. }
  1273. void flush()
  1274. {
  1275. shaderQuadQueue.flush();
  1276. currentShader.clearShader (shaderQuadQueue);
  1277. JUCE_CHECK_OPENGL_ERROR
  1278. }
  1279. void setShader (ShaderPrograms::ShaderBase& shader)
  1280. {
  1281. currentShader.setShader (target, shaderQuadQueue, shader);
  1282. JUCE_CHECK_OPENGL_ERROR
  1283. }
  1284. void setShaderForGradientFill (const ColourGradient& g, const AffineTransform& transform,
  1285. int maskTextureID, const Rectangle<int>* maskArea)
  1286. {
  1287. JUCE_CHECK_OPENGL_ERROR
  1288. activeTextures.disableTextures (shaderQuadQueue);
  1289. blendMode.setPremultipliedBlendingMode (shaderQuadQueue);
  1290. JUCE_CHECK_OPENGL_ERROR
  1291. if (maskArea != nullptr)
  1292. {
  1293. activeTextures.setTexturesEnabled (shaderQuadQueue, 3);
  1294. activeTextures.setActiveTexture (1);
  1295. activeTextures.bindTexture ((GLuint) maskTextureID);
  1296. activeTextures.setActiveTexture (0);
  1297. textureCache.bindTextureForGradient (activeTextures, g);
  1298. }
  1299. else
  1300. {
  1301. activeTextures.setSingleTextureMode (shaderQuadQueue);
  1302. textureCache.bindTextureForGradient (activeTextures, g);
  1303. }
  1304. auto t = transform.translated (0.5f - (float) target.bounds.getX(),
  1305. 0.5f - (float) target.bounds.getY());
  1306. auto p1 = g.point1.transformedBy (t);
  1307. auto p2 = g.point2.transformedBy (t);
  1308. auto p3 = Point<float> (g.point1.x + (g.point2.y - g.point1.y),
  1309. g.point1.y - (g.point2.x - g.point1.x)).transformedBy (t);
  1310. auto programs = currentShader.programs;
  1311. const ShaderPrograms::MaskedShaderParams* maskParams = nullptr;
  1312. if (g.isRadial)
  1313. {
  1314. ShaderPrograms::RadialGradientParams* gradientParams;
  1315. if (maskArea == nullptr)
  1316. {
  1317. setShader (programs->radialGradient);
  1318. gradientParams = &programs->radialGradient.gradientParams;
  1319. }
  1320. else
  1321. {
  1322. setShader (programs->radialGradientMasked);
  1323. gradientParams = &programs->radialGradientMasked.gradientParams;
  1324. maskParams = &programs->radialGradientMasked.maskParams;
  1325. }
  1326. gradientParams->setMatrix (p1, p2, p3);
  1327. }
  1328. else
  1329. {
  1330. p1 = Line<float> (p1, p3).findNearestPointTo (p2);
  1331. const Point<float> delta (p2.x - p1.x, p1.y - p2.y);
  1332. const ShaderPrograms::LinearGradientParams* gradientParams;
  1333. float grad, length;
  1334. if (std::abs (delta.x) < std::abs (delta.y))
  1335. {
  1336. if (maskArea == nullptr)
  1337. {
  1338. setShader (programs->linearGradient1);
  1339. gradientParams = &(programs->linearGradient1.gradientParams);
  1340. }
  1341. else
  1342. {
  1343. setShader (programs->linearGradient1Masked);
  1344. gradientParams = &(programs->linearGradient1Masked.gradientParams);
  1345. maskParams = &programs->linearGradient1Masked.maskParams;
  1346. }
  1347. grad = delta.x / delta.y;
  1348. length = (p2.y - grad * p2.x) - (p1.y - grad * p1.x);
  1349. }
  1350. else
  1351. {
  1352. if (maskArea == nullptr)
  1353. {
  1354. setShader (programs->linearGradient2);
  1355. gradientParams = &(programs->linearGradient2.gradientParams);
  1356. }
  1357. else
  1358. {
  1359. setShader (programs->linearGradient2Masked);
  1360. gradientParams = &(programs->linearGradient2Masked.gradientParams);
  1361. maskParams = &programs->linearGradient2Masked.maskParams;
  1362. }
  1363. grad = delta.y / delta.x;
  1364. length = (p2.x - grad * p2.y) - (p1.x - grad * p1.y);
  1365. }
  1366. gradientParams->gradientInfo.set (p1.x, p1.y, grad, length);
  1367. }
  1368. if (maskParams != nullptr)
  1369. maskParams->setBounds (*maskArea, target, 1);
  1370. JUCE_CHECK_OPENGL_ERROR
  1371. }
  1372. void setShaderForTiledImageFill (const TextureInfo& textureInfo, const AffineTransform& transform,
  1373. int maskTextureID, const Rectangle<int>* maskArea, bool isTiledFill)
  1374. {
  1375. blendMode.setPremultipliedBlendingMode (shaderQuadQueue);
  1376. auto programs = currentShader.programs;
  1377. const ShaderPrograms::MaskedShaderParams* maskParams = nullptr;
  1378. const ShaderPrograms::ImageParams* imageParams;
  1379. if (maskArea != nullptr)
  1380. {
  1381. activeTextures.setTwoTextureMode (shaderQuadQueue, textureInfo.textureID, (GLuint) maskTextureID);
  1382. if (isTiledFill)
  1383. {
  1384. setShader (programs->tiledImageMasked);
  1385. imageParams = &programs->tiledImageMasked.imageParams;
  1386. maskParams = &programs->tiledImageMasked.maskParams;
  1387. }
  1388. else
  1389. {
  1390. setShader (programs->imageMasked);
  1391. imageParams = &programs->imageMasked.imageParams;
  1392. maskParams = &programs->imageMasked.maskParams;
  1393. }
  1394. }
  1395. else
  1396. {
  1397. activeTextures.setSingleTextureMode (shaderQuadQueue);
  1398. activeTextures.bindTexture (textureInfo.textureID);
  1399. if (isTiledFill)
  1400. {
  1401. setShader (programs->tiledImage);
  1402. imageParams = &programs->tiledImage.imageParams;
  1403. }
  1404. else
  1405. {
  1406. setShader (programs->image);
  1407. imageParams = &programs->image.imageParams;
  1408. }
  1409. }
  1410. imageParams->setMatrix (transform, textureInfo, (float) target.bounds.getX(), (float) target.bounds.getY(), isTiledFill);
  1411. if (maskParams != nullptr)
  1412. maskParams->setBounds (*maskArea, target, 1);
  1413. }
  1414. Target target;
  1415. StateHelpers::BlendingMode blendMode;
  1416. StateHelpers::ActiveTextures activeTextures;
  1417. StateHelpers::TextureCache textureCache;
  1418. StateHelpers::CurrentShader currentShader;
  1419. StateHelpers::ShaderQuadQueue shaderQuadQueue;
  1420. CachedImageList::Ptr cachedImageList;
  1421. private:
  1422. GLuint previousFrameBufferTarget;
  1423. SavedBinding<TraitsVAO> savedVAOBinding;
  1424. };
  1425. //==============================================================================
  1426. struct SavedState : public RenderingHelpers::SavedStateBase<SavedState>
  1427. {
  1428. using BaseClass = RenderingHelpers::SavedStateBase<SavedState>;
  1429. SavedState (GLState* s) : BaseClass (s->target.bounds), state (s)
  1430. {}
  1431. SavedState (const SavedState& other)
  1432. : BaseClass (other), font (other.font), state (other.state),
  1433. transparencyLayer (other.transparencyLayer),
  1434. previousTarget (createCopyIfNotNull (other.previousTarget.get()))
  1435. {}
  1436. SavedState* beginTransparencyLayer (float opacity)
  1437. {
  1438. auto* s = new SavedState (*this);
  1439. if (clip != nullptr)
  1440. {
  1441. auto clipBounds = clip->getClipBounds();
  1442. state->flush();
  1443. s->transparencyLayer = Image (OpenGLImageType().create (Image::ARGB, clipBounds.getWidth(), clipBounds.getHeight(), true));
  1444. s->previousTarget.reset (new Target (state->target));
  1445. state->target = Target (state->target.context, *OpenGLImageType::getFrameBufferFrom (s->transparencyLayer), clipBounds.getPosition());
  1446. s->transparencyLayerAlpha = opacity;
  1447. s->cloneClipIfMultiplyReferenced();
  1448. s->state->target.makeActive();
  1449. }
  1450. return s;
  1451. }
  1452. void endTransparencyLayer (SavedState& finishedLayerState)
  1453. {
  1454. if (clip != nullptr)
  1455. {
  1456. jassert (finishedLayerState.previousTarget != nullptr);
  1457. state->flush();
  1458. state->target = *finishedLayerState.previousTarget;
  1459. finishedLayerState.previousTarget.reset();
  1460. state->target.makeActive();
  1461. auto clipBounds = clip->getClipBounds();
  1462. clip->renderImageUntransformed (*this, finishedLayerState.transparencyLayer,
  1463. (int) (finishedLayerState.transparencyLayerAlpha * 255.0f),
  1464. clipBounds.getX(), clipBounds.getY(), false);
  1465. }
  1466. }
  1467. using GlyphCacheType = RenderingHelpers::GlyphCache<RenderingHelpers::CachedGlyphEdgeTable<SavedState>, SavedState>;
  1468. void drawGlyph (int glyphNumber, const AffineTransform& trans)
  1469. {
  1470. if (clip != nullptr)
  1471. {
  1472. if (trans.isOnlyTranslation() && ! transform.isRotated)
  1473. {
  1474. auto& cache = GlyphCacheType::getInstance();
  1475. Point<float> pos (trans.getTranslationX(), trans.getTranslationY());
  1476. if (transform.isOnlyTranslated)
  1477. {
  1478. cache.drawGlyph (*this, font, glyphNumber, pos + transform.offset.toFloat());
  1479. }
  1480. else
  1481. {
  1482. pos = transform.transformed (pos);
  1483. Font f (font);
  1484. f.setHeight (font.getHeight() * transform.complexTransform.mat11);
  1485. auto xScale = transform.complexTransform.mat00 / transform.complexTransform.mat11;
  1486. if (std::abs (xScale - 1.0f) > 0.01f)
  1487. f.setHorizontalScale (xScale);
  1488. cache.drawGlyph (*this, f, glyphNumber, pos);
  1489. }
  1490. }
  1491. else
  1492. {
  1493. auto fontHeight = font.getHeight();
  1494. auto t = transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
  1495. .followedBy (trans));
  1496. const std::unique_ptr<EdgeTable> et (font.getTypefacePtr()->getEdgeTableForGlyph (glyphNumber, t, fontHeight));
  1497. if (et != nullptr)
  1498. fillShape (*new EdgeTableRegionType (*et), false);
  1499. }
  1500. }
  1501. }
  1502. Rectangle<int> getMaximumBounds() const { return state->target.bounds; }
  1503. void setFillType (const FillType& newFill)
  1504. {
  1505. BaseClass::setFillType (newFill);
  1506. state->textureCache.resetGradient();
  1507. }
  1508. //==============================================================================
  1509. template <typename IteratorType>
  1510. void renderImageTransformed (IteratorType& iter, const Image& src, int alpha,
  1511. const AffineTransform& trans, Graphics::ResamplingQuality, bool tiledFill) const
  1512. {
  1513. state->shaderQuadQueue.flush();
  1514. state->setShaderForTiledImageFill (state->cachedImageList->getTextureFor (src), trans, 0, nullptr, tiledFill);
  1515. state->shaderQuadQueue.add (iter, PixelARGB ((uint8) alpha, (uint8) alpha, (uint8) alpha, (uint8) alpha));
  1516. state->shaderQuadQueue.flush();
  1517. state->currentShader.clearShader (state->shaderQuadQueue);
  1518. }
  1519. template <typename IteratorType>
  1520. void renderImageUntransformed (IteratorType& iter, const Image& src, int alpha, int x, int y, bool tiledFill) const
  1521. {
  1522. renderImageTransformed (iter, src, alpha, AffineTransform::translation ((float) x, (float) y),
  1523. Graphics::lowResamplingQuality, tiledFill);
  1524. }
  1525. template <typename IteratorType>
  1526. void fillWithSolidColour (IteratorType& iter, PixelARGB colour, bool replaceContents) const
  1527. {
  1528. if (! isUsingCustomShader)
  1529. {
  1530. state->activeTextures.disableTextures (state->shaderQuadQueue);
  1531. state->blendMode.setBlendMode (state->shaderQuadQueue, replaceContents);
  1532. state->setShader (state->currentShader.programs->solidColourProgram);
  1533. }
  1534. state->shaderQuadQueue.add (iter, colour);
  1535. }
  1536. template <typename IteratorType>
  1537. void fillWithGradient (IteratorType& iter, ColourGradient& gradient, const AffineTransform& trans, bool /*isIdentity*/) const
  1538. {
  1539. state->setShaderForGradientFill (gradient, trans, 0, nullptr);
  1540. state->shaderQuadQueue.add (iter, fillType.colour.getPixelARGB());
  1541. }
  1542. void fillRectWithCustomShader (OpenGLRendering::ShaderPrograms::ShaderBase& shader, Rectangle<int> area)
  1543. {
  1544. state->setShader (shader);
  1545. isUsingCustomShader = true;
  1546. fillRect (area, true);
  1547. isUsingCustomShader = false;
  1548. state->currentShader.clearShader (state->shaderQuadQueue);
  1549. }
  1550. //==============================================================================
  1551. Font font;
  1552. GLState* state;
  1553. bool isUsingCustomShader = false;
  1554. private:
  1555. Image transparencyLayer;
  1556. std::unique_ptr<Target> previousTarget;
  1557. SavedState& operator= (const SavedState&);
  1558. };
  1559. //==============================================================================
  1560. struct ShaderContext : public RenderingHelpers::StackBasedLowLevelGraphicsContext<SavedState>
  1561. {
  1562. ShaderContext (const Target& target) : glState (target)
  1563. {
  1564. stack.initialise (new SavedState (&glState));
  1565. }
  1566. void fillRectWithCustomShader (ShaderPrograms::ShaderBase& shader, Rectangle<int> area)
  1567. {
  1568. static_cast<SavedState&> (*stack).fillRectWithCustomShader (shader, area);
  1569. }
  1570. GLState glState;
  1571. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ShaderContext)
  1572. };
  1573. struct NonShaderContext : public LowLevelGraphicsSoftwareRenderer
  1574. {
  1575. NonShaderContext (const Target& t, const Image& im)
  1576. : LowLevelGraphicsSoftwareRenderer (im), target (t), image (im)
  1577. {}
  1578. ~NonShaderContext()
  1579. {
  1580. JUCE_CHECK_OPENGL_ERROR
  1581. auto previousFrameBufferTarget = OpenGLFrameBuffer::getCurrentFrameBufferTarget();
  1582. #if ! JUCE_ANDROID
  1583. target.context.extensions.glActiveTexture (GL_TEXTURE0);
  1584. if (! target.context.isCoreProfile())
  1585. glEnable (GL_TEXTURE_2D);
  1586. clearGLError();
  1587. #endif
  1588. OpenGLTexture texture;
  1589. texture.loadImage (image);
  1590. texture.bind();
  1591. target.makeActive();
  1592. target.context.copyTexture (target.bounds, Rectangle<int> (texture.getWidth(),
  1593. texture.getHeight()),
  1594. target.bounds.getWidth(), target.bounds.getHeight(),
  1595. false);
  1596. glBindTexture (GL_TEXTURE_2D, 0);
  1597. #if JUCE_WINDOWS
  1598. if (target.context.extensions.glBindFramebuffer != nullptr)
  1599. #endif
  1600. target.context.extensions.glBindFramebuffer (GL_FRAMEBUFFER, previousFrameBufferTarget);
  1601. JUCE_CHECK_OPENGL_ERROR
  1602. }
  1603. private:
  1604. Target target;
  1605. Image image;
  1606. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NonShaderContext)
  1607. };
  1608. static void clearOpenGLGlyphCacheCallback()
  1609. {
  1610. SavedState::GlyphCacheType::getInstance().reset();
  1611. }
  1612. static std::unique_ptr<LowLevelGraphicsContext> createOpenGLContext (const Target& target)
  1613. {
  1614. clearOpenGLGlyphCache = clearOpenGLGlyphCacheCallback;
  1615. if (target.context.areShadersAvailable())
  1616. return std::make_unique<ShaderContext> (target);
  1617. Image tempImage (Image::ARGB, target.bounds.getWidth(), target.bounds.getHeight(), true, SoftwareImageType());
  1618. return std::make_unique<NonShaderContext> (target, tempImage);
  1619. }
  1620. }
  1621. //==============================================================================
  1622. std::unique_ptr<LowLevelGraphicsContext> createOpenGLGraphicsContext (OpenGLContext& context, int width, int height)
  1623. {
  1624. return createOpenGLGraphicsContext (context, context.getFrameBufferID(), width, height);
  1625. }
  1626. std::unique_ptr<LowLevelGraphicsContext> createOpenGLGraphicsContext (OpenGLContext& context, OpenGLFrameBuffer& target)
  1627. {
  1628. return OpenGLRendering::createOpenGLContext (OpenGLRendering::Target (context, target, {}));
  1629. }
  1630. std::unique_ptr<LowLevelGraphicsContext> createOpenGLGraphicsContext (OpenGLContext& context, unsigned int frameBufferID, int width, int height)
  1631. {
  1632. return OpenGLRendering::createOpenGLContext (OpenGLRendering::Target (context, frameBufferID, width, height));
  1633. }
  1634. //==============================================================================
  1635. struct CustomProgram : public ReferenceCountedObject,
  1636. public OpenGLRendering::ShaderPrograms::ShaderBase
  1637. {
  1638. CustomProgram (OpenGLRendering::ShaderContext& c, const String& fragmentShader)
  1639. : ShaderBase (c.glState.target.context, fragmentShader.toRawUTF8())
  1640. {
  1641. }
  1642. static ReferenceCountedObjectPtr<CustomProgram> get (const String& hashName)
  1643. {
  1644. if (auto* c = OpenGLContext::getCurrentContext())
  1645. if (auto* o = c->getAssociatedObject (hashName.toRawUTF8()))
  1646. return *static_cast<CustomProgram*> (o);
  1647. return {};
  1648. }
  1649. static ReferenceCountedObjectPtr<CustomProgram> getOrCreate (LowLevelGraphicsContext& gc, const String& hashName,
  1650. const String& code, String& errorMessage)
  1651. {
  1652. if (auto c = get (hashName))
  1653. return c;
  1654. if (auto* sc = dynamic_cast<OpenGLRendering::ShaderContext*> (&gc))
  1655. {
  1656. ReferenceCountedObjectPtr<CustomProgram> c (new CustomProgram (*sc, code));
  1657. errorMessage = c->lastError;
  1658. if (errorMessage.isEmpty())
  1659. {
  1660. if (auto context = OpenGLContext::getCurrentContext())
  1661. {
  1662. context->setAssociatedObject (hashName.toRawUTF8(), c.get());
  1663. return c;
  1664. }
  1665. }
  1666. }
  1667. return nullptr;
  1668. }
  1669. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomProgram)
  1670. };
  1671. OpenGLGraphicsContextCustomShader::OpenGLGraphicsContextCustomShader (const String& fragmentShaderCode)
  1672. : code (String (JUCE_DECLARE_VARYING_COLOUR
  1673. JUCE_DECLARE_VARYING_PIXELPOS
  1674. "\n#define pixelAlpha frontColour.a\n") + fragmentShaderCode),
  1675. hashName (String::toHexString (fragmentShaderCode.hashCode64()) + "_shader")
  1676. {
  1677. }
  1678. OpenGLGraphicsContextCustomShader::~OpenGLGraphicsContextCustomShader()
  1679. {
  1680. if (OpenGLContext* context = OpenGLContext::getCurrentContext())
  1681. context->setAssociatedObject (hashName.toRawUTF8(), nullptr);
  1682. }
  1683. OpenGLShaderProgram* OpenGLGraphicsContextCustomShader::getProgram (LowLevelGraphicsContext& gc) const
  1684. {
  1685. String errorMessage;
  1686. if (auto c = CustomProgram::getOrCreate (gc, hashName, code, errorMessage))
  1687. return &(c->program);
  1688. return {};
  1689. }
  1690. void OpenGLGraphicsContextCustomShader::fillRect (LowLevelGraphicsContext& gc, Rectangle<int> area) const
  1691. {
  1692. String errorMessage;
  1693. if (auto sc = dynamic_cast<OpenGLRendering::ShaderContext*> (&gc))
  1694. {
  1695. if (auto c = CustomProgram::getOrCreate (gc, hashName, code, errorMessage))
  1696. {
  1697. c->onShaderActivated = onShaderActivated;
  1698. sc->fillRectWithCustomShader (*c, area);
  1699. }
  1700. }
  1701. }
  1702. Result OpenGLGraphicsContextCustomShader::checkCompilation (LowLevelGraphicsContext& gc)
  1703. {
  1704. String errorMessage;
  1705. if (CustomProgram::getOrCreate (gc, hashName, code, errorMessage) != nullptr)
  1706. return Result::ok();
  1707. return Result::fail (errorMessage);
  1708. }
  1709. } // namespace juce