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.

1929 lines
68KB

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