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.

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