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.

1895 lines
68KB

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