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.

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