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.

1930 lines
68KB

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