The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1929 lines
68KB

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