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.

1922 lines
68KB

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