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.

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