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.

1478 lines
56KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021-2024 Filipe Coelho <falktx@falktx.com>
  4. * SPDX-License-Identifier: GPL-3.0-or-later
  5. */
  6. #define STDIO_OVERRIDE Rackdep
  7. #include <cstdio>
  8. #include <cstring>
  9. #include <list>
  10. #include <string>
  11. namespace rack {
  12. namespace plugin {
  13. void updateStaticPluginsDarkMode();
  14. }
  15. namespace settings {
  16. int rateLimit = 0;
  17. extern bool preferDarkPanels;
  18. extern std::string uiTheme;
  19. }
  20. namespace ui {
  21. void refreshTheme();
  22. }
  23. }
  24. #include "nanovg.h"
  25. // Special nvgRGB for blank panels
  26. extern "C" {
  27. NVGcolor nvgRGBblank(const unsigned char r, const unsigned char g, const unsigned char b)
  28. {
  29. return rack::settings::preferDarkPanels ? nvgRGB(0x20, 0x20, 0x20) : nvgRGB(r, g, b);
  30. }
  31. }
  32. // Compile those nice implementation-in-header little libraries
  33. #define NANOSVG_IMPLEMENTATION
  34. #define NANOSVG_ALL_COLOR_KEYWORDS
  35. #undef nsvgDelete
  36. #undef nsvgParseFromFile
  37. #include <nanosvg.h>
  38. #ifndef HEADLESS
  39. enum DarkMode {
  40. kMode21kHz,
  41. kModeAaronStatic,
  42. kModeAlefsbits,
  43. kModeAlgoritmarte,
  44. kModeArableInstruments,
  45. kModeAudibleInstruments,
  46. kModeBidoo,
  47. kModeCf,
  48. kModeComputerscare,
  49. kModeDHE,
  50. kModeESeries,
  51. kModeLilacLoop,
  52. kModeLittleUtils,
  53. kModeKocmoc,
  54. kModeMyth,
  55. kModeNonlinearcircuits,
  56. kModeParableInstruments,
  57. kModePdArray,
  58. kModePathSet,
  59. kModeVoxglitch,
  60. kModeWhatTheRack,
  61. };
  62. // Custom Cardinal filtering
  63. static const struct {
  64. const DarkMode mode;
  65. const char* const filename;
  66. const char* shapeIdsToIgnore[5];
  67. const int shapeNumberToIgnore;
  68. } svgFilesToInvertForDarkMode[] = {
  69. // MIT
  70. { kMode21kHz, "/21kHz/res/Panels/D_Inf.svg", {}, -1 },
  71. { kMode21kHz, "/21kHz/res/Panels/PalmLoop.svg", {}, -1 },
  72. { kMode21kHz, "/21kHz/res/Panels/TachyonEntangler.svg", {}, -1 },
  73. // MIT
  74. { kModeAaronStatic, "/AaronStatic/res/ChordCV.svg", {}, -1 },
  75. { kModeAaronStatic, "/AaronStatic/res/DiatonicCV.svg", {}, -1 },
  76. { kModeAaronStatic, "/AaronStatic/res/RandomNoteCV.svg", {}, -1 },
  77. { kModeAaronStatic, "/AaronStatic/res/ScaleCV.svg", {}, -1 },
  78. // GPL3.0-or-later
  79. { kModeAlefsbits, "/alefsbits/res/blank6hp.svg", {}, -1 },
  80. { kModeAlefsbits, "/alefsbits/res/fibb.svg", {}, -1 },
  81. { kModeAlefsbits, "/alefsbits/res/logic.svg", {}, -1 },
  82. { kModeAlefsbits, "/alefsbits/res/math.svg", {}, -1 },
  83. { kModeAlefsbits, "/alefsbits/res/mlt.svg", {}, -1 },
  84. { kModeAlefsbits, "/alefsbits/res/noize.svg", {}, -1 },
  85. { kModeAlefsbits, "/alefsbits/res/octsclr.svg", {}, -1 },
  86. { kModeAlefsbits, "/alefsbits/res/polyrand.svg", {}, -1 },
  87. { kModeAlefsbits, "/alefsbits/res/shift.svg", {}, -1 },
  88. { kModeAlefsbits, "/alefsbits/res/simplexandhold.svg", {}, -1 },
  89. { kModeAlefsbits, "/alefsbits/res/simplexvco.svg", {}, -1 },
  90. { kModeAlefsbits, "/alefsbits/res/steps.svg", {}, -1 },
  91. { kModeAlefsbits, "/alefsbits/res/switch.svg", {}, -1 },
  92. // GPL3.0-or-later
  93. { kModeAlgoritmarte, "/Algoritmarte/res/Clockkky.svg", {}, -1 },
  94. { kModeAlgoritmarte, "/Algoritmarte/res/CyclicCA.svg", {}, -1 },
  95. { kModeAlgoritmarte, "/Algoritmarte/res/HoldMeTight.svg", {}, -1 },
  96. { kModeAlgoritmarte, "/Algoritmarte/res/MusiFrog.svg", {}, -1 },
  97. { kModeAlgoritmarte, "/Algoritmarte/res/MusiMath.svg", {}, -1 },
  98. { kModeAlgoritmarte, "/Algoritmarte/res/Planetz.svg", {}, -1 },
  99. { kModeAlgoritmarte, "/Algoritmarte/res/Zefiro.svg", {}, -1 },
  100. // Custom, runtime dark mode used with permission
  101. { kModeAudibleInstruments, "/AudibleInstruments/res/Blinds.svg", {}, -1 },
  102. { kModeAudibleInstruments, "/AudibleInstruments/res/Braids.svg", {}, -1 },
  103. { kModeAudibleInstruments, "/AudibleInstruments/res/Branches.svg", {}, -1 },
  104. { kModeAudibleInstruments, "/AudibleInstruments/res/Clouds.svg", {}, -1 },
  105. { kModeAudibleInstruments, "/AudibleInstruments/res/Elements.svg", {}, -1 },
  106. { kModeAudibleInstruments, "/AudibleInstruments/res/Frames.svg", {}, -1 },
  107. { kModeAudibleInstruments, "/AudibleInstruments/res/Kinks.svg", {}, -1 },
  108. { kModeAudibleInstruments, "/AudibleInstruments/res/Links.svg", {}, -1 },
  109. { kModeAudibleInstruments, "/AudibleInstruments/res/Marbles.svg", {}, -1 },
  110. { kModeAudibleInstruments, "/AudibleInstruments/res/Peaks.svg", {}, -1 },
  111. { kModeAudibleInstruments, "/AudibleInstruments/res/Plaits.svg", {}, -1 },
  112. { kModeAudibleInstruments, "/AudibleInstruments/res/Rings.svg", {}, -1 },
  113. { kModeAudibleInstruments, "/AudibleInstruments/res/Ripples.svg", {}, -1 },
  114. { kModeAudibleInstruments, "/AudibleInstruments/res/Shades.svg", {}, -1 },
  115. { kModeAudibleInstruments, "/AudibleInstruments/res/Sheep.svg", {}, -1 },
  116. { kModeAudibleInstruments, "/AudibleInstruments/res/Shelves.svg", {}, -1 },
  117. { kModeAudibleInstruments, "/AudibleInstruments/res/Stages.svg", {}, -1 },
  118. { kModeAudibleInstruments, "/AudibleInstruments/res/Streams.svg", {}, -1 },
  119. { kModeAudibleInstruments, "/AudibleInstruments/res/Tides.svg", {}, -1 },
  120. { kModeAudibleInstruments, "/AudibleInstruments/res/Tides2.svg", {}, -1 },
  121. { kModeAudibleInstruments, "/AudibleInstruments/res/Veils.svg", {}, -1 },
  122. { kModeAudibleInstruments, "/AudibleInstruments/res/Warps.svg", {}, -1 },
  123. // CC-BY-NC-ND-4.0, runtime dark mode used with permission
  124. { kModeBidoo, "/Bidoo/res/ACNE.svg", {}, -1 },
  125. { kModeBidoo, "/Bidoo/res/ANTN.svg", {}, -1 },
  126. { kModeBidoo, "/Bidoo/res/BAFIS.svg", {}, -1 },
  127. { kModeBidoo, "/Bidoo/res/BANCAU.svg", {}, -1 },
  128. { kModeBidoo, "/Bidoo/res/BAR.svg", {"rect833"}, -1 },
  129. { kModeBidoo, "/Bidoo/res/BISTROT.svg", {}, -1 },
  130. { kModeBidoo, "/Bidoo/res/BORDL.svg", {"rect959-3-0-7-5","rect959-3-0-7","rect959-3-0","rect959-3"}, -1 },
  131. { kModeBidoo, "/Bidoo/res/CANARD.svg", {"rect959-3-7"}, -1 },
  132. { kModeBidoo, "/Bidoo/res/CHUTE.svg", {}, -1 },
  133. { kModeBidoo, "/Bidoo/res/DFUZE.svg", {}, -1 },
  134. { kModeBidoo, "/Bidoo/res/DIKTAT.svg", {"rect843","rect843-0","rect843-0-8"}, -1 },
  135. { kModeBidoo, "/Bidoo/res/DILEMO.svg", {}, -1 },
  136. { kModeBidoo, "/Bidoo/res/DTROY.svg", {"rect959-3"}, -1 },
  137. { kModeBidoo, "/Bidoo/res/DUKE.svg", {}, -1 },
  138. { kModeBidoo, "/Bidoo/res/EDSAROS.svg", {"rect959-3-7","rect959-3-7-8","rect959-3-7-8-1","rect959-3-7-8-1-4"}, -1 },
  139. { kModeBidoo, "/Bidoo/res/EMILE.svg", {}, -1 },
  140. { kModeBidoo, "/Bidoo/res/ENCORE.svg", {}, -1 },
  141. { kModeBidoo, "/Bidoo/res/ENCOREExpander.svg", {}, -1 },
  142. { kModeBidoo, "/Bidoo/res/FLAME.svg", {}, -1 },
  143. { kModeBidoo, "/Bidoo/res/FORK.svg", {}, -1 },
  144. { kModeBidoo, "/Bidoo/res/FREIN.svg", {}, -1 },
  145. { kModeBidoo, "/Bidoo/res/HCTIP.svg", {}, -1 },
  146. { kModeBidoo, "/Bidoo/res/HUITRE.svg", {}, -1 },
  147. { kModeBidoo, "/Bidoo/res/LAMBDA.svg", {}, -1 },
  148. { kModeBidoo, "/Bidoo/res/LATE.svg", {}, -1 },
  149. { kModeBidoo, "/Bidoo/res/LIMBO.svg", {}, -1 },
  150. { kModeBidoo, "/Bidoo/res/LIMONADE.svg", {"rect839","rect839-6"}, -1 },
  151. { kModeBidoo, "/Bidoo/res/LOURDE.svg", {"rect847","rect847-7","rect847-5","rect847-3"}, -1 },
  152. { kModeBidoo, "/Bidoo/res/MAGMA.svg", {}, -1 },
  153. { kModeBidoo, "/Bidoo/res/MINIBAR.svg", {"rect833"}, -1 },
  154. { kModeBidoo, "/Bidoo/res/MOIRE.svg", {"rect843","rect843-7"}, -1 },
  155. { kModeBidoo, "/Bidoo/res/MS.svg", {}, -1 },
  156. { kModeBidoo, "/Bidoo/res/MU.svg", {"rect864"}, -1 },
  157. { kModeBidoo, "/Bidoo/res/OAI.svg", {}, -1 },
  158. { kModeBidoo, "/Bidoo/res/OUAIVE.svg", {"rect959-3-7"}, -1 },
  159. { kModeBidoo, "/Bidoo/res/PERCO.svg", {}, -1 },
  160. { kModeBidoo, "/Bidoo/res/PILOT.svg", {"rect843-6-4-5","rect843","rect843-4","rect843-6-4","rect843-6-7"}, -1 },
  161. { kModeBidoo, "/Bidoo/res/POUPRE.svg", {}, -1 },
  162. { kModeBidoo, "/Bidoo/res/RABBIT.svg", {}, -1 },
  163. { kModeBidoo, "/Bidoo/res/RATEAU.svg", {"rect930"}, -1 },
  164. { kModeBidoo, "/Bidoo/res/REI.svg", {}, -1 },
  165. { kModeBidoo, "/Bidoo/res/SIGMA.svg", {}, -1 },
  166. { kModeBidoo, "/Bidoo/res/SPORE.svg", {}, -1 },
  167. { kModeBidoo, "/Bidoo/res/TIARE.svg", {}, -1 },
  168. { kModeBidoo, "/Bidoo/res/TOCANTE.svg", {"rect843"}, -1 },
  169. { kModeBidoo, "/Bidoo/res/VOID.svg", {}, -1 },
  170. { kModeBidoo, "/Bidoo/res/ZINC.svg", {}, -1 },
  171. { kModeBidoo, "/Bidoo/res/ZOUMAI.svg", {}, -1 },
  172. { kModeBidoo, "/Bidoo/res/ZOUMAIExpander.svg", {}, -1 },
  173. // BSD-3-Clause
  174. { kModeCf, "/cf/res/ALGEBRA.svg", {}, -1 },
  175. { kModeCf, "/cf/res/BUFFER.svg", {}, -1 },
  176. { kModeCf, "/cf/res/CHOKE.svg", {}, -1 },
  177. { kModeCf, "/cf/res/CUBE.svg", {}, -1 },
  178. { kModeCf, "/cf/res/CUTS.svg", {}, -1 },
  179. { kModeCf, "/cf/res/DISTO.svg", {}, -1 },
  180. { kModeCf, "/cf/res/EACH.svg", {}, -1 },
  181. { kModeCf, "/cf/res/FOUR.svg", {}, -1 },
  182. { kModeCf, "/cf/res/FUNKTION.svg", {}, -1 },
  183. { kModeCf, "/cf/res/L3DS3Q.svg", {}, 3 },
  184. { kModeCf, "/cf/res/LABEL.svg", {}, -1 },
  185. { kModeCf, "/cf/res/LEDS.svg", {}, -1 },
  186. { kModeCf, "/cf/res/LEDSEQ.svg", {}, 3 },
  187. { kModeCf, "/cf/res/MASTER.svg", {}, -1 },
  188. { kModeCf, "/cf/res/METRO.svg", {}, -1 },
  189. { kModeCf, "/cf/res/MONO.svg", {}, -1 },
  190. { kModeCf, "/cf/res/PATCH.svg", {}, -1 },
  191. { kModeCf, "/cf/res/PEAK.svg", {}, -1 },
  192. { kModeCf, "/cf/res/PLAY.svg", {}, -1 },
  193. { kModeCf, "/cf/res/PLAYER.svg", {}, -1 },
  194. { kModeCf, "/cf/res/SLIDERSEQ.svg", {}, -1 },
  195. { kModeCf, "/cf/res/STEPS.svg", {}, -1 },
  196. { kModeCf, "/cf/res/STEREO.svg", {}, -1 },
  197. { kModeCf, "/cf/res/SUB.svg", {}, -1 },
  198. { kModeCf, "/cf/res/trSEQ.svg", {}, -1 },
  199. { kModeCf, "/cf/res/VARIABLE.svg", {}, -1 },
  200. // BSD-3 Clause
  201. { kModeComputerscare, "/Computerscare/res/computerscare-clk-text.svg", {}, -1},
  202. { kModeComputerscare, "/Computerscare/res/vertical-x-1.svg", {}, -1},
  203. { kModeComputerscare, "/Computerscare/res/vertical-x-2.svg", {}, -1},
  204. { kModeComputerscare, "/Computerscare/res/vertical-x-3.svg", {}, -1},
  205. { kModeComputerscare, "/Computerscare/res/computerscare-logo-normal.svg", {}, -1},
  206. { kModeComputerscare, "/Computerscare/res/computerscare-logo-sad.svg", {}, -1},
  207. { kModeComputerscare, "/Computerscare/res/ComputerscareDebugPanel.svg", {}, -1 },
  208. { kModeComputerscare, "/Computerscare/res/ComputerscareOhPeasPanel.svg", {}, -1 },
  209. { kModeComputerscare, "/Computerscare/res/ComputerscareFolyPacePanel.svg", {}, -1 },
  210. { kModeComputerscare, "/Computerscare/res/ComputerscareKnolyPobsPanel.svg", {}, -1 },
  211. { kModeComputerscare, "/Computerscare/res/ComputerscareTolyPoolsPanel.svg", {}, -1 },
  212. { kModeComputerscare, "/Computerscare/res/ComputerscareMolyPatrixPanel.svg", {}, -1 },
  213. { kModeComputerscare, "/Computerscare/res/ComputerscareRolyPouterPanel.svg", {}, -1 },
  214. { kModeComputerscare, "/Computerscare/res/ComputerscareBolyPuttonsPanel.svg", {}, -1 },
  215. { kModeComputerscare, "/Computerscare/res/ComputerscareCustomBlankPanel.svg", {}, -1 },
  216. { kModeComputerscare, "/Computerscare/res/ComputerscareLaundrySoupPanel.svg", {}, -1 },
  217. { kModeComputerscare, "/Computerscare/res/ComputerscareILoveCookiesPanel.svg", {}, -1 },
  218. { kModeComputerscare, "/Computerscare/res/ComputerscareGolyPeneratorPanel.svg", {}, -1 },
  219. { kModeComputerscare, "/Computerscare/res/ComputerscareSolyPequencerPanel.svg", {}, -1 },
  220. { kModeComputerscare, "/Computerscare/res/ComputerscarePatchSequencerPanel.svg", {}, -1 },
  221. { kModeComputerscare, "/Computerscare/res/ComputerscareHorseADoodleDooPanel.svg", {}, -1 },
  222. { kModeComputerscare, "/Computerscare/res/ComputerscareCustomBlankExpanderPanel.svg", {}, -1 },
  223. // MIT
  224. { kModeDHE, "/DHE-Modules/svg/blossom.svg", {}, -1 },
  225. { kModeDHE, "/DHE-Modules/svg/booster-stage.svg", {}, -1 },
  226. { kModeDHE, "/DHE-Modules/svg/buttons.svg", {}, -1 },
  227. { kModeDHE, "/DHE-Modules/svg/cubic.svg", {}, -1 },
  228. { kModeDHE, "/DHE-Modules/svg/curve-sequencer-4.svg", {}, -1 },
  229. { kModeDHE, "/DHE-Modules/svg/curve-sequencer-8.svg", {}, -1 },
  230. { kModeDHE, "/DHE-Modules/svg/curve-sequencer-16.svg", {}, -1 },
  231. { kModeDHE, "/DHE-Modules/svg/func.svg", {}, -1 },
  232. { kModeDHE, "/DHE-Modules/svg/func-6.svg", {}, -1 },
  233. { kModeDHE, "/DHE-Modules/svg/fuzzy-logic-h.svg", {}, -1 },
  234. { kModeDHE, "/DHE-Modules/svg/fuzzy-logic-z.svg", {}, -1 },
  235. { kModeDHE, "/DHE-Modules/svg/gator.svg", {}, -1 },
  236. { kModeDHE, "/DHE-Modules/svg/hostage.svg", {}, -1 },
  237. { kModeDHE, "/DHE-Modules/svg/ranger.svg", {}, -1 },
  238. { kModeDHE, "/DHE-Modules/svg/scannibal-4.svg", {}, -1 },
  239. { kModeDHE, "/DHE-Modules/svg/scannibal-8.svg", {}, -1 },
  240. { kModeDHE, "/DHE-Modules/svg/scannibal-16.svg", {}, -1 },
  241. { kModeDHE, "/DHE-Modules/svg/sequencizer-4.svg", {}, -1 },
  242. { kModeDHE, "/DHE-Modules/svg/sequencizer-8.svg", {}, -1 },
  243. { kModeDHE, "/DHE-Modules/svg/sequencizer-16.svg", {}, -1 },
  244. { kModeDHE, "/DHE-Modules/svg/stage.svg", {}, -1 },
  245. { kModeDHE, "/DHE-Modules/svg/swave.svg", {}, -1 },
  246. { kModeDHE, "/DHE-Modules/svg/tapers.svg", {}, -1 },
  247. { kModeDHE, "/DHE-Modules/svg/truth-2.svg", {}, -1 },
  248. { kModeDHE, "/DHE-Modules/svg/truth-3.svg", {}, -1 },
  249. { kModeDHE, "/DHE-Modules/svg/truth-4.svg", {}, -1 },
  250. { kModeDHE, "/DHE-Modules/svg/upstage.svg", {}, -1 },
  251. { kModeDHE, "/DHE-Modules/svg/xycloid.svg", {}, -1 },
  252. // Custom, runtime dark mode used with permission
  253. { kModeESeries,"/ESeries/res/E340.svg", {}, -1 },
  254. // GPL3.0-or-later
  255. { kModeLilacLoop, "/LilacLoop/res/Looper.svg", {}, -1 },
  256. // EUPL-1.2
  257. { kModeLittleUtils, "/LittleUtils/res/Bias_Semitone.svg", {}, -1 },
  258. { kModeLittleUtils, "/LittleUtils/res/ButtonModule.svg", {}, -1 },
  259. { kModeLittleUtils, "/LittleUtils/res/MulDiv.svg", {}, -1 },
  260. { kModeLittleUtils, "/LittleUtils/res/PulseGenerator.svg", {}, -1 },
  261. { kModeLittleUtils, "/LittleUtils/res/TeleportIn.svg", {}, -1 },
  262. { kModeLittleUtils, "/LittleUtils/res/TeleportOut.svg", {}, -1 },
  263. // GPL-3.0-or-later
  264. { kModeKocmoc, "/kocmoc/res/DDLY.svg", {}, -1 },
  265. { kModeKocmoc, "/kocmoc/res/DIOD.svg", {}, -1 },
  266. { kModeKocmoc, "/kocmoc/res/LADR.svg", {}, -1 },
  267. { kModeKocmoc, "/kocmoc/res/uLADR.svg", {}, -1 },
  268. { kModeKocmoc, "/kocmoc/res/MUL.svg", {}, -1 },
  269. { kModeKocmoc, "/kocmoc/res/OP.svg", {}, -1 },
  270. { kModeKocmoc, "/kocmoc/res/PHASR.svg", {}, -1 },
  271. { kModeKocmoc, "/kocmoc/res/SKF.svg", {}, -1 },
  272. { kModeKocmoc, "/kocmoc/res/SVF.svg", {}, -1 },
  273. { kModeKocmoc, "/kocmoc/res/uSVF.svg", {}, -1 },
  274. { kModeKocmoc, "/kocmoc/res/TRG.svg", {}, -1 },
  275. // GPL-3.0-or-later
  276. { kModeMyth, "/myth-modules/res/Mavka.svg", {}, -1 },
  277. { kModeMyth, "/myth-modules/res/Molphar.svg", {}, -1 },
  278. // CC0-1.0
  279. { kModeNonlinearcircuits, "/nonlinearcircuits/res/BOOLs.svg", {}, -1 },
  280. { kModeNonlinearcircuits, "/nonlinearcircuits/res/DoubleNeuron.svg", {}, -1 },
  281. { kModeNonlinearcircuits, "/nonlinearcircuits/res/LetsSplosh.svg", {}, -1 },
  282. { kModeNonlinearcircuits, "/nonlinearcircuits/res/4seq.svg", {}, -1 },
  283. { kModeNonlinearcircuits, "/nonlinearcircuits/res/8BitCipher.svg", {}, -1 },
  284. { kModeNonlinearcircuits, "/nonlinearcircuits/res/DivideConquer.svg", {}, -1 },
  285. { kModeNonlinearcircuits, "/nonlinearcircuits/res/DivineCMOS.svg", {}, -1 },
  286. { kModeNonlinearcircuits, "/nonlinearcircuits/res/GENiE.svg", {}, -1 },
  287. { kModeNonlinearcircuits, "/nonlinearcircuits/res/Neuron.svg", {}, -1 },
  288. { kModeNonlinearcircuits, "/nonlinearcircuits/res/Numberwang.svg", {}, -1 },
  289. { kModeNonlinearcircuits, "/nonlinearcircuits/res/Router.svg", {}, -1 },
  290. { kModeNonlinearcircuits, "/nonlinearcircuits/res/Segue.svg", {}, -1 },
  291. { kModeNonlinearcircuits, "/nonlinearcircuits/res/Statues.svg", {}, -1 },
  292. { kModeNonlinearcircuits, "/nonlinearcircuits/res/SquidAxon.svg", {}, -1 },
  293. { kModeNonlinearcircuits, "/nonlinearcircuits/res/SlothApathy.svg", {}, -1 },
  294. { kModeNonlinearcircuits, "/nonlinearcircuits/res/SlothInertia.svg", {}, -1 },
  295. { kModeNonlinearcircuits, "/nonlinearcircuits/res/SlothTorpor.svg", {}, -1 },
  296. { kModeNonlinearcircuits, "/nonlinearcircuits/res/TripleSloth.svg", {}, -1 },
  297. // GPL-3.0-or-later
  298. { kModePathSet, "/PathSet/res/AstroVibe.svg", {}, -1 },
  299. { kModePathSet, "/PathSet/res/GlassPane.svg", {}, -1 },
  300. { kModePathSet, "/PathSet/res/IceTray.svg", {}, -1 },
  301. { kModePathSet, "/PathSet/res/Nudge.svg", {}, -1 },
  302. { kModePathSet, "/PathSet/res/OneShot.svg", {}, -1 },
  303. { kModePathSet, "/PathSet/res/PlusPane.svg", {}, -1 },
  304. { kModePathSet, "/PathSet/res/ShiftyExpander.svg", {}, -1 },
  305. { kModePathSet, "/PathSet/res/ShiftyMod.svg", {}, -1 },
  306. // EUPL-1.2
  307. { kModePdArray, "/PdArray/res/Array.svg", {}, -1 },
  308. { kModePdArray, "/PdArray/res/Miniramp.svg", {}, -1 },
  309. { kModePdArray, "/PdArray/res/Ministep.svg", {}, -1 },
  310. // BSD-3-Clause
  311. { kModeVoxglitch, "/voxglitch/res/autobreak_front_panel.svg", {}, -1 },
  312. { kModeVoxglitch, "/voxglitch/res/bytebeat_front_panel.svg", {}, -1 },
  313. { kModeVoxglitch, "/voxglitch/res/digital_programmer_front_panel.svg", {}, -1 },
  314. { kModeVoxglitch, "/voxglitch/res/digital_sequencer_front_panel.svg", {}, -1 },
  315. { kModeVoxglitch, "/voxglitch/res/digital_sequencer_xp_front_panel.svg", {}, -1 },
  316. { kModeVoxglitch, "/voxglitch/res/ghosts_front_panel.svg", {}, -1 },
  317. { kModeVoxglitch, "/voxglitch/res/glitch_sequencer_front_panel.svg", {}, -1 },
  318. { kModeVoxglitch, "/voxglitch/res/goblins_front_panel.svg", {}, -1 },
  319. { kModeVoxglitch, "/voxglitch/res/grain_engine_mk2_expander_front_panel.svg", {}, -1 },
  320. { kModeVoxglitch, "/voxglitch/res/grain_engine_mk2_front_panel_r3.svg", {}, -1 },
  321. { kModeVoxglitch, "/voxglitch/res/grain_fx_front_panel.svg", {}, -1 },
  322. { kModeVoxglitch, "/voxglitch/res/hazumi_front_panel.svg", {}, -1 },
  323. { kModeVoxglitch, "/voxglitch/res/looper_front_panel.svg", {}, -1 },
  324. { kModeVoxglitch, "/voxglitch/res/repeater_front_panel.svg", {}, -1 },
  325. { kModeVoxglitch, "/voxglitch/res/samplerx8_front_panel.svg", {}, -1 },
  326. { kModeVoxglitch, "/voxglitch/res/satanonaut_front_panel.svg", {}, -1 },
  327. { kModeVoxglitch, "/voxglitch/res/wav_bank_front_panel.svg", {}, -1 },
  328. { kModeVoxglitch, "/voxglitch/res/wav_bank_mc_front_panel_v2.svg", {}, -1 },
  329. { kModeVoxglitch, "/voxglitch/res/xy_front_panel.svg", {}, -1 },
  330. // WTFPL
  331. { kModeWhatTheRack, "/WhatTheRack/res/WhatTheJack.svg", {}, -1 },
  332. { kModeWhatTheRack, "/WhatTheRack/res/WhatTheMod.svg", {}, -1 },
  333. { kModeWhatTheRack, "/WhatTheRack/res/WhatTheRack.svg", {}, -1 },
  334. };
  335. enum LightMode {
  336. kMode8Mode,
  337. kModeAutinn,
  338. kModeBefaco,
  339. kModeCardinal,
  340. kModeComputerscareLight,
  341. kModeExtratone,
  342. kModeFehlerFabrik,
  343. kModeForsitan,
  344. kModeFundamental,
  345. kModeGoodSheperd,
  346. kModeH4n4,
  347. kModeHamptonHarmonics,
  348. kModeLomas,
  349. kModeMockba,
  350. kModeMog,
  351. kModePrism,
  352. kModeRepelzen,
  353. kModeSonusmodular,
  354. kModeWSTDDrums,
  355. };
  356. static const struct {
  357. const LightMode mode;
  358. const char* const filename;
  359. } svgFilesToInvertForLightMode[] = {
  360. // BSD
  361. { kMode8Mode, "/8Mode/res/SNsoft_Panel.svg" },
  362. // GPLv3+
  363. { kModeAutinn, "/Autinn/res/AmpModule.svg" },
  364. { kModeAutinn, "/Autinn/res/BassModule.svg" },
  365. { kModeAutinn, "/Autinn/res/CVConverterModule.svg" },
  366. { kModeAutinn, "/Autinn/res/ComponentLibrary" },
  367. { kModeAutinn, "/Autinn/res/DeadbandModule.svg" },
  368. { kModeAutinn, "/Autinn/res/DigiModule.svg" },
  369. { kModeAutinn, "/Autinn/res/DiseeModule.svg" },
  370. { kModeAutinn, "/Autinn/res/FilModule.svg" },
  371. { kModeAutinn, "/Autinn/res/FlopperModule.svg" },
  372. { kModeAutinn, "/Autinn/res/ImpModule.svg" },
  373. { kModeAutinn, "/Autinn/res/JetteModule.svg" },
  374. { kModeAutinn, "/Autinn/res/MelodyModule.svg" },
  375. { kModeAutinn, "/Autinn/res/MeraModule.svg" },
  376. { kModeAutinn, "/Autinn/res/Mixer6Module.svg" },
  377. { kModeAutinn, "/Autinn/res/NapModule.svg" },
  378. { kModeAutinn, "/Autinn/res/NonModule.svg" },
  379. { kModeAutinn, "/Autinn/res/OxcartModule.svg" },
  380. { kModeAutinn, "/Autinn/res/RebelModule.svg" },
  381. { kModeAutinn, "/Autinn/res/RetriModule.svg" },
  382. { kModeAutinn, "/Autinn/res/SawModule.svg" },
  383. { kModeAutinn, "/Autinn/res/SjipModule.svg" },
  384. { kModeAutinn, "/Autinn/res/SquareModule.svg" },
  385. { kModeAutinn, "/Autinn/res/VibratoModule.svg" },
  386. { kModeAutinn, "/Autinn/res/VxyModule.svg" },
  387. { kModeAutinn, "/Autinn/res/ZodModule.svg" },
  388. // Custom, runtime light mode used with permission
  389. { kModeBefaco, "/Befaco/res/components/Knurlie.svg" },
  390. { kModeBefaco, "/Befaco/res/panels/ABC.svg" },
  391. { kModeBefaco, "/Befaco/res/panels/ADSR.svg" },
  392. { kModeBefaco, "/Befaco/res/panels/Burst.svg" },
  393. { kModeBefaco, "/Befaco/res/panels/ChoppingKinky.svg" },
  394. { kModeBefaco, "/Befaco/res/panels/DualAtenuverter.svg" },
  395. { kModeBefaco, "/Befaco/res/panels/EvenVCO.svg" },
  396. { kModeBefaco, "/Befaco/res/panels/HexmixVCA.svg" },
  397. { kModeBefaco, "/Befaco/res/panels/Kickall.svg" },
  398. { kModeBefaco, "/Befaco/res/panels/Mex.svg" },
  399. { kModeBefaco, "/Befaco/res/panels/Mixer.svg" },
  400. { kModeBefaco, "/Befaco/res/panels/Morphader.svg" },
  401. { kModeBefaco, "/Befaco/res/panels/MotionMTR.svg" },
  402. { kModeBefaco, "/Befaco/res/panels/Muxlicer.svg" },
  403. { kModeBefaco, "/Befaco/res/panels/NoisePlethora.svg" },
  404. { kModeBefaco, "/Befaco/res/panels/Percall.svg" },
  405. { kModeBefaco, "/Befaco/res/panels/PonyVCO.svg" },
  406. { kModeBefaco, "/Befaco/res/panels/Rampage.svg" },
  407. { kModeBefaco, "/Befaco/res/panels/STMix.svg" },
  408. { kModeBefaco, "/Befaco/res/panels/SamplingModulator.svg" },
  409. { kModeBefaco, "/Befaco/res/panels/SlewLimiter.svg" },
  410. { kModeBefaco, "/Befaco/res/panels/SpringReverb.svg" },
  411. { kModeBefaco, "/Befaco/res/panels/StereoStrip.svg" },
  412. { kModeBefaco, "/Befaco/res/panels/Voltio.svg" },
  413. { kModeBefaco, "/Befaco/res/panels/Octaves.svg" },
  414. { kModeBefaco, "/Befaco/res/panels/Bypass.svg" },
  415. { kModeBefaco, "/Befaco/res/panels/Bandit.svg" },
  416. { kModeBefaco, "/Befaco/res/panels/Atte.svg" },
  417. { kModeBefaco, "/Befaco/res/panels/AxBC.svg" },
  418. { kModeBefaco, "/Befaco/res/panels/Mixer2.svg" },
  419. { kModeBefaco, "/Befaco/res/panels/MuDi.svg" },
  420. { kModeBefaco, "/Befaco/res/panels/Slew.svg" },
  421. // GPLv3+
  422. { kModeCardinal, "/Cardinal/res/AudioFile.svg" },
  423. { kModeCardinal, "/Cardinal/res/AudioToCVPitch.svg" },
  424. { kModeCardinal, "/Cardinal/res/Carla.svg" },
  425. { kModeCardinal, "/Cardinal/res/ExpanderMIDI.svg" },
  426. { kModeCardinal, "/Cardinal/res/glBars.svg" },
  427. { kModeCardinal, "/Cardinal/res/HostAudio.svg" },
  428. { kModeCardinal, "/Cardinal/res/HostCV.svg" },
  429. { kModeCardinal, "/Cardinal/res/HostMIDI.svg" },
  430. { kModeCardinal, "/Cardinal/res/HostMIDICC.svg" },
  431. { kModeCardinal, "/Cardinal/res/HostMIDIGate.svg" },
  432. { kModeCardinal, "/Cardinal/res/HostMIDIMap.svg" },
  433. { kModeCardinal, "/Cardinal/res/HostParameters.svg" },
  434. { kModeCardinal, "/Cardinal/res/HostParamsMap.svg" },
  435. { kModeCardinal, "/Cardinal/res/HostTime.svg" },
  436. { kModeCardinal, "/Cardinal/res/Ildaeil.svg" },
  437. // BSD-3 Clause
  438. { kModeComputerscareLight, "/Computerscare/res/ComputerscareStolyFickPigurePanel.svg" },
  439. // GPLv3+
  440. { kModeExtratone, "/Extratone/res/Darwinism.svg" },
  441. // { kModeExtratone, "/Extratone/res/HalluciMemory.svg" },
  442. { kModeExtratone, "/Extratone/res/Ichneumonid.svg" },
  443. { kModeExtratone, "/Extratone/res/Meganeura.svg" },
  444. { kModeExtratone, "/Extratone/res/Mesoglea.svg" },
  445. { kModeExtratone, "/Extratone/res/Mesoglea2.svg" },
  446. { kModeExtratone, "/Extratone/res/Mesohyl.svg" },
  447. { kModeExtratone, "/Extratone/res/Modulo.svg" },
  448. { kModeExtratone, "/Extratone/res/Opabinia.svg" },
  449. { kModeExtratone, "/Extratone/res/Pureneura.svg" },
  450. { kModeExtratone, "/Extratone/res/Puzzlebox.svg" },
  451. { kModeExtratone, "/Extratone/res/Splitterburst.svg" },
  452. { kModeExtratone, "/Extratone/res/XtrtnBlank.svg" },
  453. // GPLv3+
  454. { kModeFehlerFabrik, "/FehlerFabrik/res/Arpanet.svg" },
  455. { kModeFehlerFabrik, "/FehlerFabrik/res/Aspect.svg" },
  456. { kModeFehlerFabrik, "/FehlerFabrik/res/Botzinger.svg" },
  457. { kModeFehlerFabrik, "/FehlerFabrik/res/Chi.svg" },
  458. { kModeFehlerFabrik, "/FehlerFabrik/res/Components" },
  459. { kModeFehlerFabrik, "/FehlerFabrik/res/Fax.svg" },
  460. { kModeFehlerFabrik, "/FehlerFabrik/res/Lilt.svg" },
  461. { kModeFehlerFabrik, "/FehlerFabrik/res/Luigi.svg" },
  462. { kModeFehlerFabrik, "/FehlerFabrik/res/Monte.svg" },
  463. { kModeFehlerFabrik, "/FehlerFabrik/res/Nova.svg" },
  464. { kModeFehlerFabrik, "/FehlerFabrik/res/Planck.svg" },
  465. { kModeFehlerFabrik, "/FehlerFabrik/res/PSIOP.svg" },
  466. { kModeFehlerFabrik, "/FehlerFabrik/res/Rasoir.svg" },
  467. { kModeFehlerFabrik, "/FehlerFabrik/res/Sigma.svg" },
  468. { kModeFehlerFabrik, "/FehlerFabrik/res/Components/FFHexScrew.svg" },
  469. // GPLv3+
  470. { kModeForsitan, "/forsitan-modulare/res/alea.svg" },
  471. { kModeForsitan, "/forsitan-modulare/res/cumuli.svg" },
  472. { kModeForsitan, "/forsitan-modulare/res/deinde.svg" },
  473. { kModeForsitan, "/forsitan-modulare/res/interea.svg" },
  474. { kModeForsitan, "/forsitan-modulare/res/palette.svg" },
  475. { kModeForsitan, "/forsitan-modulare/res/pavo.svg" },
  476. // GPLv3+
  477. { kModeFundamental, "/Fundamental/res/8vert.svg" },
  478. { kModeFundamental, "/Fundamental/res/ADSR.svg" },
  479. { kModeFundamental, "/Fundamental/res/Delay.svg" },
  480. { kModeFundamental, "/Fundamental/res/LFO.svg" },
  481. { kModeFundamental, "/Fundamental/res/Merge.svg" },
  482. { kModeFundamental, "/Fundamental/res/MidSide.svg" },
  483. { kModeFundamental, "/Fundamental/res/Mixer.svg" },
  484. { kModeFundamental, "/Fundamental/res/Mutes.svg" },
  485. { kModeFundamental, "/Fundamental/res/Noise.svg" },
  486. { kModeFundamental, "/Fundamental/res/Octave.svg" },
  487. { kModeFundamental, "/Fundamental/res/Pulses.svg" },
  488. { kModeFundamental, "/Fundamental/res/Quantizer.svg" },
  489. { kModeFundamental, "/Fundamental/res/Random.svg" },
  490. { kModeFundamental, "/Fundamental/res/SEQ3.svg" },
  491. { kModeFundamental, "/Fundamental/res/Scope.svg" },
  492. { kModeFundamental, "/Fundamental/res/SequentialSwitch1.svg" },
  493. { kModeFundamental, "/Fundamental/res/SequentialSwitch2.svg" },
  494. { kModeFundamental, "/Fundamental/res/Split.svg" },
  495. { kModeFundamental, "/Fundamental/res/Sum.svg" },
  496. { kModeFundamental, "/Fundamental/res/VCA-1.svg" },
  497. { kModeFundamental, "/Fundamental/res/VCA.svg" },
  498. { kModeFundamental, "/Fundamental/res/VCF.svg" },
  499. { kModeFundamental, "/Fundamental/res/VCMixer.svg" },
  500. { kModeFundamental, "/Fundamental/res/VCO.svg" },
  501. { kModeFundamental, "/Fundamental/res/WTLFO.svg" },
  502. { kModeFundamental, "/Fundamental/res/WTVCO.svg" },
  503. { kModeFundamental, "/Fundamental/res/components/ADSR-bg.svg" },
  504. { kModeFundamental, "/Fundamental/res/components/Scope-bg.svg" },
  505. // GPLv3+
  506. { kModeGoodSheperd, "/GoodSheperd/res/Holzschnabel.svg" },
  507. { kModeGoodSheperd, "/GoodSheperd/res/Hurdle.svg" },
  508. { kModeGoodSheperd, "/GoodSheperd/res/SEQ3st.svg" },
  509. { kModeGoodSheperd, "/GoodSheperd/res/Seqtrol.svg" },
  510. { kModeGoodSheperd, "/GoodSheperd/res/Stable16.svg" },
  511. { kModeGoodSheperd, "/GoodSheperd/res/Stall.svg" },
  512. { kModeGoodSheperd, "/GoodSheperd/res/Switch1.svg" },
  513. { kModeGoodSheperd, "/GoodSheperd/res/components/SquareSwitch_0.svg" },
  514. { kModeGoodSheperd, "/GoodSheperd/res/components/SquareSwitch_1.svg" },
  515. // GPLv3+
  516. { kModeH4n4, "/h4n4-modules/res/XenQnt.svg" },
  517. // MIT
  518. { kModeHamptonHarmonics, "/HamptonHarmonics/res/Arp.svg" },
  519. { kModeHamptonHarmonics, "/HamptonHarmonics/res/Progress.svg" },
  520. // GPLv3+
  521. { kModeLomas, "/LomasModules/res/AdvancedSampler.svg" },
  522. { kModeLomas, "/LomasModules/res/GateSequencer.svg" },
  523. // MIT
  524. { kModeMockba, "/MockbaModular/res/Empty_gray.svg" },
  525. // CC0
  526. { kModeMog, "/Mog/res/Network.svg" },
  527. { kModeMog, "/Mog/res/Nexus.svg" },
  528. // CC-BY-SA-4.0
  529. { kModePrism, "/Prism/res/prism_Droplet.svg" },
  530. { kModePrism, "/Prism/res/prism_Rainbow.svg" },
  531. { kModePrism, "/Prism/res/RainbowScaleExpander.svg" },
  532. // CC-BY-SA-4.0
  533. { kModeRepelzen, "/repelzen/res/reface/reburst_bg.svg" },
  534. { kModeRepelzen, "/repelzen/res/reface/refold_bg.svg" },
  535. { kModeRepelzen, "/repelzen/res/reface/repelzen_bg1.svg" },
  536. { kModeRepelzen, "/repelzen/res/reface/repelzen_bg2.svg" },
  537. { kModeRepelzen, "/repelzen/res/reface/repelzen_bg3.svg" },
  538. { kModeRepelzen, "/repelzen/res/reface/repelzen_bg4.svg" },
  539. { kModeRepelzen, "/repelzen/res/reface/repelzen_bg5.svg" },
  540. { kModeRepelzen, "/repelzen/res/reface/repelzen_bg6.svg" },
  541. { kModeRepelzen, "/repelzen/res/reface/repelzen_bg7.svg" },
  542. { kModeRepelzen, "/repelzen/res/reface/repelzen_bg8.svg" },
  543. { kModeRepelzen, "/repelzen/res/reface/retrig_bg.svg" },
  544. { kModeRepelzen, "/repelzen/res/reface/rewin_bg.svg" },
  545. { kModeRepelzen, "/repelzen/res/reface/rexmix_bg.svg" },
  546. // GPLv3+
  547. { kModeSonusmodular, "/sonusmodular/res/addiction.svg" },
  548. { kModeSonusmodular, "/sonusmodular/res/bitter.svg" },
  549. { kModeSonusmodular, "/sonusmodular/res/bymidside.svg" },
  550. { kModeSonusmodular, "/sonusmodular/res/campione.svg" },
  551. { kModeSonusmodular, "/sonusmodular/res/chainsaw.svg" },
  552. { kModeSonusmodular, "/sonusmodular/res/ctrl.svg" },
  553. { kModeSonusmodular, "/sonusmodular/res/deathcrush.svg" },
  554. { kModeSonusmodular, "/sonusmodular/res/fraction.svg" },
  555. { kModeSonusmodular, "/sonusmodular/res/harmony.svg" },
  556. { kModeSonusmodular, "/sonusmodular/res/ladrone.svg" },
  557. { kModeSonusmodular, "/sonusmodular/res/luppolo.svg" },
  558. { kModeSonusmodular, "/sonusmodular/res/luppolo3.svg" },
  559. { kModeSonusmodular, "/sonusmodular/res/micromacro.svg" },
  560. { kModeSonusmodular, "/sonusmodular/res/mrcheb.svg" },
  561. { kModeSonusmodular, "/sonusmodular/res/multimulti.svg" },
  562. { kModeSonusmodular, "/sonusmodular/res/neurosc.svg" },
  563. { kModeSonusmodular, "/sonusmodular/res/oktagon.svg" },
  564. { kModeSonusmodular, "/sonusmodular/res/osculum.svg" },
  565. { kModeSonusmodular, "/sonusmodular/res/paramath.svg" },
  566. { kModeSonusmodular, "/sonusmodular/res/piconoise.svg" },
  567. { kModeSonusmodular, "/sonusmodular/res/pith.svg" },
  568. { kModeSonusmodular, "/sonusmodular/res/pusher.svg" },
  569. { kModeSonusmodular, "/sonusmodular/res/ringo.svg" },
  570. { kModeSonusmodular, "/sonusmodular/res/scramblase.svg" },
  571. { kModeSonusmodular, "/sonusmodular/res/tropicana.svg" },
  572. { kModeSonusmodular, "/sonusmodular/res/twoff.svg" },
  573. { kModeSonusmodular, "/sonusmodular/res/yabp.svg" },
  574. // CC0-1.0
  575. { kModeWSTDDrums, "/WSTD-Drums/res/Baronial.svg" },
  576. { kModeWSTDDrums, "/WSTD-Drums/res/BD9.svg" },
  577. { kModeWSTDDrums, "/WSTD-Drums/res/ClosedHH.svg" },
  578. { kModeWSTDDrums, "/WSTD-Drums/res/CR78.svg" },
  579. { kModeWSTDDrums, "/WSTD-Drums/res/DMX.svg" },
  580. { kModeWSTDDrums, "/WSTD-Drums/res/Gnome.svg" },
  581. { kModeWSTDDrums, "/WSTD-Drums/res/Marionette.svg" },
  582. { kModeWSTDDrums, "/WSTD-Drums/res/OpenHH.svg" },
  583. { kModeWSTDDrums, "/WSTD-Drums/res/SBD.svg" },
  584. { kModeWSTDDrums, "/WSTD-Drums/res/Sequencer.svg" },
  585. { kModeWSTDDrums, "/WSTD-Drums/res/Snare.svg" },
  586. { kModeWSTDDrums, "/WSTD-Drums/res/Tomi.svg" },
  587. { kModeWSTDDrums, "/WSTD-Drums/res/component/Knob.svg" },
  588. };
  589. static inline
  590. unsigned int darkerColor(const unsigned int color) noexcept
  591. {
  592. return (color & 0xff000000)
  593. | (std::max<int>(0, ((color & 0xff0000) >> 16) - 0x80) << 16)
  594. | (std::max<int>(0, ((color & 0xff00) >> 8) - 0x80) << 8)
  595. | (std::max<int>(0, (color & 0xff) - 0x80));
  596. }
  597. static inline
  598. unsigned int lighterColor(const unsigned int color) noexcept
  599. {
  600. return (color & 0xff000000)
  601. | (std::min<int>(0xff, ((color & 0xff0000) >> 16) + 0x80) << 16)
  602. | (std::min<int>(0xff, ((color & 0xff00) >> 8) + 0x80) << 8)
  603. | (std::min<int>(0xff, (color & 0xff) + 0x80));
  604. }
  605. static inline
  606. unsigned int invertColor(const unsigned int color) noexcept
  607. {
  608. return (color & 0xff000000)
  609. | (0xff0000 - (color & 0xff0000))
  610. | (0xff00 - (color & 0xff00))
  611. | (0xff - (color & 0xff));
  612. }
  613. static inline
  614. bool invertPaintForDarkMode(const DarkMode mode, NSVGshape* const shape, NSVGpaint& paint, const char* const svgFileToInvert)
  615. {
  616. if (paint.type == NSVG_PAINT_LINEAR_GRADIENT)
  617. {
  618. switch (mode)
  619. {
  620. // Special case for PathSet shifty gradient
  621. case kModePathSet:
  622. paint.gradient->stops[0].color = 0xff7c4919; // 50% darker than main blue
  623. paint.gradient->stops[1].color = 0xff5b3a1a; // 33.3% darker than main blue
  624. return false;
  625. default:
  626. break;
  627. }
  628. }
  629. if (paint.type == NSVG_PAINT_NONE)
  630. return true;
  631. if (paint.type != NSVG_PAINT_COLOR)
  632. return false;
  633. switch (mode)
  634. {
  635. // Special case for Bidoo red color
  636. case kModeBidoo:
  637. if (paint.color == 0xff001fcd)
  638. {
  639. paint.color = 0xcf8b94c4;
  640. return true;
  641. }
  642. if (paint.color == 0xff000000 && shape->stroke.type == NSVG_PAINT_COLOR)
  643. {
  644. switch (shape->stroke.color)
  645. {
  646. case 0xff777777:
  647. case 0xff7c7c7c:
  648. case 0xff828282:
  649. case 0xffb1b1b1:
  650. case 0xffb2b2b2:
  651. return false;
  652. }
  653. }
  654. break;
  655. // Special case for Computerscare
  656. case kModeComputerscare:
  657. switch (paint.color)
  658. {
  659. // Just make vertical-x-* visible
  660. case 0xffa6c924:
  661. return false;
  662. }
  663. break;
  664. // Special case for DHE, mark things darker instead of inverting
  665. case kModeDHE:
  666. paint.color = darkerColor(paint.color);
  667. return true;
  668. // Special case for Lilac
  669. case kModeLilacLoop:
  670. switch (paint.color)
  671. {
  672. // main bg (custom)
  673. case 0xffd5d5da:
  674. paint.color = 0xff242228;
  675. return true;
  676. // main color (do nothing)
  677. case 0xffbfb7d7:
  678. return false;
  679. // screws (hide)
  680. case 0xffc8c8cf:
  681. case 0xffbcbcbc:
  682. case 0xffb1b1bb:
  683. case 0xffacacac:
  684. case 0xff898991:
  685. case 0xff727272:
  686. paint.color = 0x00000000;
  687. return true;
  688. }
  689. break;
  690. // Special case for Nonlinear Circuits
  691. case kModeNonlinearcircuits:
  692. switch (paint.color)
  693. {
  694. case 0xff9a7900:
  695. case 0xff96782c:
  696. case 0xff6a07ae:
  697. case 0xffcf8044:
  698. case 0xff2ac6ba:
  699. case 0xff5ba85c:
  700. case 0xffa97b00:
  701. case 0xff9f7a00:
  702. case 0xffff7300:
  703. case 0xffa47b00:
  704. case 0xffb09423:
  705. return false;
  706. case 0xffffffff:
  707. paint.color = 0x00000000;
  708. return true;
  709. }
  710. break;
  711. // Special case for PathSet colors
  712. case kModePathSet:
  713. // only invert main colors for Nudge.svg
  714. if (std::strcmp(svgFileToInvert, "/PathSet/res/Nudge.svg") == 0)
  715. {
  716. switch (paint.color)
  717. {
  718. case 0xffe6e6e6:
  719. paint.color = 0xff191919;
  720. return true;
  721. case 0xff000000:
  722. paint.color = 0xffffffff;
  723. return true;
  724. default:
  725. return false;
  726. }
  727. }
  728. // everything else
  729. else
  730. {
  731. switch (paint.color)
  732. {
  733. // main blue tone
  734. case 0xffdf7a1a:
  735. case 0xffe78a31:
  736. case 0xffc26a16:
  737. if (shape->opacity == 0.5f && std::strcmp(svgFileToInvert, "/PathSet/res/AstroVibe.svg") == 0)
  738. {
  739. shape->opacity = 0.2f;
  740. return true;
  741. }
  742. if (shape->opacity == 0.25f)
  743. shape->opacity = 0.75f;
  744. return false;
  745. // bottom logo stuff, set to full white
  746. case 0xff000000:
  747. if (shape->stroke.type != NSVG_PAINT_NONE)
  748. {
  749. paint.color = 0xffffffff;
  750. return true;
  751. }
  752. break;
  753. /*
  754. // OneShot beverage
  755. case 0xff021755:
  756. case 0xff03299a:
  757. case 0xff032ba2:
  758. return false;
  759. */
  760. // pink step 2 (pink with 50% opacity on bg)
  761. case 0xffef73ea:
  762. paint.color = 0xff812d7d;
  763. return true;
  764. // pink step 3 (pink with 33.3% opacity on bg)
  765. case 0xfff49ff0:
  766. paint.color = 0xff4d234c;
  767. return true;
  768. // pink and orange
  769. case 0xffe941e2:
  770. case 0xff698efb:
  771. // pink and orange (translucent)
  772. case 0x4de941e2:
  773. case 0x62698efb:
  774. return false;
  775. // blue darker 1 (blue with 50% opacity on bg)
  776. case 0xffde944f:
  777. case 0xffe3b080:
  778. case 0xffe4cbb3:
  779. case 0xfff5c99f:
  780. case 0xfff6d1b0:
  781. paint.color = 0xff7c4919;
  782. return true;
  783. // blue darker 2 (blue with 33.3% opacity on bg)
  784. case 0xffe5d9cd:
  785. case 0xfff8dcc2:
  786. case 0xffe1a265:
  787. paint.color = 0xff5b3a1a;
  788. return true;
  789. // blue darker 3 (blue with 25% opacity on bg)
  790. case 0xffe5cbb3:
  791. paint.color = 0xff4b321a;
  792. return true;
  793. }
  794. }
  795. break;
  796. // Special case for voxglitch colors
  797. case kModeVoxglitch:
  798. switch (paint.color)
  799. {
  800. // wavbank blue
  801. case 0xffc5ae8a:
  802. // various black
  803. case 0xff121212:
  804. case 0xff2a2828:
  805. return false;
  806. // satanonaut
  807. case 0xff595959:
  808. paint.color = 0x7f3219ac;
  809. return true;
  810. }
  811. break;
  812. default:
  813. break;
  814. }
  815. switch (paint.color)
  816. {
  817. // scopes or other special things (do nothing)
  818. case 0x40ffffff:
  819. case 0xff0000aa:
  820. case 0xff004200:
  821. case 0xff2b281e:
  822. case 0xff2d2827:
  823. case 0xff303030:
  824. case 0xff362c23:
  825. case 0xff40352c:
  826. case 0xff5735d9:
  827. case 0xff5935d8:
  828. case 0xff5c26d9:
  829. case 0xff6140db:
  830. case 0xffa09400:
  831. case 0xffa19400:
  832. case 0xffa7a100:
  833. case 0xffa8a200:
  834. case 0xffaba102:
  835. case 0xff22a5e9:
  836. case 0xff6140da:
  837. case 0xff119ee6:
  838. case 0xff2432ed:
  839. case 0xff0095fe:
  840. case 0xff4d9a4d:
  841. case 0xff4d4d9a:
  842. case 0xff0187fc:
  843. return false;
  844. // pure black (convert to not quite pure white)
  845. case 0xff000000:
  846. paint.color = 0xffd0d0d0;
  847. return true;
  848. // all others (direct invert)
  849. default:
  850. paint.color = invertColor(paint.color);
  851. return true;
  852. }
  853. }
  854. static inline
  855. bool invertPaintForLightMode(const LightMode mode, NSVGshape* const shape, NSVGpaint& paint)
  856. {
  857. if (paint.type == NSVG_PAINT_LINEAR_GRADIENT)
  858. {
  859. switch (mode)
  860. {
  861. case kModeFundamental:
  862. paint.gradient->stops[0].color = 0xffffffff;
  863. paint.gradient->stops[1].color = 0xffe6d6d6;
  864. return true;
  865. default:
  866. for (int i=0; i<paint.gradient->nstops; ++i)
  867. paint.gradient->stops[i].color = invertColor(paint.gradient->stops[i].color);
  868. return true;
  869. }
  870. }
  871. if (paint.type == NSVG_PAINT_RADIAL_GRADIENT && mode == kModeMog)
  872. {
  873. std::free(paint.gradient);
  874. paint.type = NSVG_PAINT_COLOR;
  875. paint.color = 0xffe5e5e5;
  876. return true;
  877. }
  878. if (paint.type == NSVG_PAINT_NONE)
  879. return true;
  880. if (paint.type != NSVG_PAINT_COLOR)
  881. return false;
  882. switch (mode)
  883. {
  884. case kMode8Mode:
  885. switch (paint.color)
  886. {
  887. case 0xff000000:
  888. if (std::strcmp(shape->id, "rect1211") == 0)
  889. break;
  890. return false;
  891. case 0xff1a1a1a:
  892. if (std::strcmp(shape->id, "rect1523") != 0)
  893. break;
  894. return false;
  895. default:
  896. return false;
  897. }
  898. break;
  899. case kModeAutinn:
  900. switch (paint.color)
  901. {
  902. // red stripe
  903. case 0xff0a115e:
  904. paint.color = 0xffa1a8f5;
  905. return true;
  906. // logo
  907. case 0xff00d7ff:
  908. paint.color = 0xff005868;
  909. return true;
  910. }
  911. break;
  912. case kModeExtratone:
  913. switch (paint.color)
  914. {
  915. case 0x00000000:
  916. case 0x00ffffff:
  917. case 0xff000000:
  918. case 0xffd5ffe5:
  919. case 0xfff2f2f2:
  920. case 0xfff2ffff:
  921. case 0xfff9f9f9:
  922. case 0xffffffff:
  923. break;
  924. default:
  925. return false;
  926. }
  927. break;
  928. case kModeFehlerFabrik:
  929. switch (paint.color)
  930. {
  931. // make a few colors reverse in luminance/lightness
  932. case 0xff3edcfc: paint.color = 0xff039fbf; return true;
  933. case 0xff4a6fff: paint.color = 0xff0024b2; return true;
  934. case 0xff5c49fd: paint.color = 0xff1502b6; return true;
  935. case 0xff61a6ff: paint.color = 0xff00459e; return true;
  936. case 0xff6e97ad: paint.color = 0xff537c93; return true;
  937. case 0xff78ffb1: paint.color = 0xff008739; return true;
  938. case 0xffb5cf00: paint.color = 0xff627000; return true;
  939. case 0xffbfa463: paint.color = 0xff9c8140; return true;
  940. case 0xffcba5e4: paint.color = 0xff411b5a; return true;
  941. case 0xffce86ef: paint.color = 0xff58107a; return true;
  942. case 0xffcf7685: paint.color = 0xff8a303e; return true;
  943. case 0xffd1e471: paint.color = 0xff798c1b; return true;
  944. // screw core
  945. case 0xff1a1a1a: paint.color = 0xffcccccc; return true;
  946. // keep already darkish colors
  947. case 0xff6a8800:
  948. case 0xff7cce00:
  949. return false;
  950. }
  951. break;
  952. case kModeFundamental:
  953. switch (paint.color)
  954. {
  955. case 0xc0000000:
  956. paint.color = 0xe6000000;
  957. return true;
  958. case 0xff909092:
  959. paint.color = 0xff000000;
  960. return true;
  961. case 0xff000000:
  962. if (shape->opacity == 0.5f)
  963. {
  964. shape->opacity = 0.9f;
  965. return false;
  966. }
  967. break;
  968. }
  969. break;
  970. case kModeGoodSheperd:
  971. switch (paint.color)
  972. {
  973. // background
  974. case 0xff332e21: paint.color = 0xffdfdacd; return true;
  975. case 0xff462f17: paint.color = 0xffe8d2ba; return true;
  976. // jack box overlays
  977. case 0xff56534a: paint.color = 0xffb6b3aa; return true;
  978. case 0xffbc9d8e: paint.color = 0xff705142; return true;
  979. case 0xfeede9e2: paint.color = 0xff1c1812; return true;
  980. // colors to keep the same
  981. case 0xff2400fe:
  982. case 0xffcab39b:
  983. return false;
  984. }
  985. break;
  986. case kModeH4n4:
  987. switch (paint.color)
  988. {
  989. case 0xffffb380:
  990. return false;
  991. case 0xffffccaa:
  992. paint.color = 0xff572300;
  993. return true;
  994. }
  995. break;
  996. case kModeMockba:
  997. switch (paint.color)
  998. {
  999. case 0xff1a1a1a:
  1000. case 0xff666666:
  1001. paint.color = 0xffd7e3f4;
  1002. return true;
  1003. default:
  1004. return false;
  1005. }
  1006. break;
  1007. case kModeMog:
  1008. switch (paint.color)
  1009. {
  1010. case 0xff442499:
  1011. case 0xff587ee2:
  1012. case 0xff1ecae8:
  1013. case 0xff2dd6ac:
  1014. case 0xffcf924c:
  1015. case 0xffd8b3bb:
  1016. case 0xff29165d:
  1017. case 0xff354d89:
  1018. case 0xff127b8d:
  1019. case 0xff1b8269:
  1020. case 0xff7e592e:
  1021. case 0xff836d72:
  1022. return false;
  1023. }
  1024. break;
  1025. case kModePrism:
  1026. switch (paint.color)
  1027. {
  1028. case 0xff000000:
  1029. case 0xff505770:
  1030. case 0xff657c9b:
  1031. case 0xff7ba357:
  1032. case 0xff7f64f2:
  1033. case 0xff99e4ff:
  1034. case 0xffa7ff6c:
  1035. case 0xffc279e2:
  1036. case 0xffe079c4:
  1037. case 0xffe5ff66:
  1038. case 0xffff88d0:
  1039. case 0xffffa369:
  1040. return false;
  1041. case 0xff0f0f0f:
  1042. if (std::strcmp(shape->id, "path10") == 0 || std::strcmp(shape->id, "circle506") == 0)
  1043. {
  1044. paint.color = 0xffffffff;
  1045. return true;
  1046. }
  1047. return false;
  1048. case 0xffbababa:
  1049. paint.color = 0xff000000;
  1050. return true;
  1051. }
  1052. break;
  1053. case kModeRepelzen:
  1054. switch (paint.color)
  1055. {
  1056. case 0xff4c4ccc:
  1057. case 0xff87a610:
  1058. case 0xffb78e09:
  1059. return false;
  1060. case 0xff44bbd8:
  1061. paint.color = 0xff228ba5;
  1062. return true;
  1063. }
  1064. break;
  1065. case kModeSonusmodular:
  1066. switch (paint.color)
  1067. {
  1068. case 0xff2a2aff:
  1069. case 0xff4e4ed3:
  1070. case 0xff55ddff:
  1071. case 0xff87cdde:
  1072. case 0xffdbdbe3:
  1073. case 0xffe9afaf:
  1074. return false;
  1075. case 0xff0a1284:
  1076. paint.color = 0xff7a82f5;
  1077. return true;
  1078. }
  1079. break;
  1080. default:
  1081. break;
  1082. }
  1083. paint.color = invertColor(paint.color);
  1084. return true;
  1085. }
  1086. #endif // HEADLESS
  1087. extern "C" {
  1088. NSVGimage* nsvgParseFromFileCardinal(const char* filename, const char* units, float dpi);
  1089. void nsvgDeleteCardinal(NSVGimage*);
  1090. }
  1091. #ifndef HEADLESS
  1092. struct ExtendedNSVGimage {
  1093. NSVGimage* const handle;
  1094. NSVGimage* handleOrig;
  1095. NSVGimage* handleMOD;
  1096. NSVGshape* shapesOrig;
  1097. NSVGshape* shapesMOD;
  1098. };
  1099. static std::list<ExtendedNSVGimage> loadedDarkSVGs;
  1100. static std::list<ExtendedNSVGimage> loadedLightSVGs;
  1101. static inline
  1102. void nsvg__duplicatePaint(NSVGpaint& dst, NSVGpaint& src)
  1103. {
  1104. if (dst.type == NSVG_PAINT_LINEAR_GRADIENT || dst.type == NSVG_PAINT_RADIAL_GRADIENT)
  1105. {
  1106. const size_t size = sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(src.gradient->nstops-1);
  1107. dst.gradient = static_cast<NSVGgradient*>(malloc(size));
  1108. std::memcpy(dst.gradient, src.gradient, size);
  1109. }
  1110. }
  1111. static inline
  1112. NSVGshape* nsvg__duplicateShapes(NSVGshape* const orig)
  1113. {
  1114. NSVGshape* const dup = static_cast<NSVGshape*>(malloc(sizeof(NSVGshape)));
  1115. std::memcpy(dup, orig, sizeof(NSVGshape));
  1116. nsvg__duplicatePaint(dup->fill, orig->fill);
  1117. nsvg__duplicatePaint(dup->stroke, orig->stroke);
  1118. for (NSVGshape* shape2 = dup;;)
  1119. {
  1120. if (shape2->next == nullptr)
  1121. break;
  1122. NSVGshape* const shapedup = static_cast<NSVGshape*>(malloc(sizeof(NSVGshape)));
  1123. std::memcpy(shapedup, shape2->next, sizeof(NSVGshape));
  1124. nsvg__duplicatePaint(shapedup->fill, shape2->next->fill);
  1125. nsvg__duplicatePaint(shapedup->stroke, shape2->next->stroke);
  1126. shape2->next = shapedup;
  1127. shape2 = shapedup;
  1128. }
  1129. return dup;
  1130. }
  1131. static inline
  1132. void deleteExtendedNSVGimage(ExtendedNSVGimage& ext)
  1133. {
  1134. if (ext.shapesMOD != nullptr)
  1135. {
  1136. // delete duplicated resources
  1137. for (NSVGshape *next, *shape = ext.shapesMOD;;)
  1138. {
  1139. next = shape->next;
  1140. nsvg__deletePaint(&shape->fill);
  1141. nsvg__deletePaint(&shape->stroke);
  1142. std::free(shape);
  1143. if (next == nullptr)
  1144. break;
  1145. shape = next;
  1146. }
  1147. // revert shapes back to original
  1148. ext.handle->shapes = ext.shapesOrig;
  1149. ext.shapesMOD = nullptr;
  1150. }
  1151. if (ext.handleMOD != nullptr)
  1152. {
  1153. nsvgDelete(ext.handleMOD);
  1154. ext.handleMOD = nullptr;
  1155. }
  1156. if (ext.handleOrig != nullptr)
  1157. {
  1158. std::memcpy(ext.handle, ext.handleOrig, sizeof(NSVGimage));
  1159. std::free(ext.handleOrig);
  1160. ext.handleOrig = nullptr;
  1161. }
  1162. }
  1163. #endif // HEADLESS
  1164. NSVGimage* nsvgParseFromFileCardinal(const char* const filename, const char* const units, const float dpi)
  1165. {
  1166. if (NSVGimage* const handle = nsvgParseFromFile(filename, units, dpi))
  1167. {
  1168. /*
  1169. if (NSVGshape* const shapes = handle->shapes)
  1170. {
  1171. for (NSVGshape *next, *shape = shapes;;)
  1172. {
  1173. next = shape->next;
  1174. nsvg__deletePaint(&shape->fill);
  1175. nsvg__deletePaint(&shape->stroke);
  1176. std::free(shape);
  1177. if (next == nullptr)
  1178. break;
  1179. shape = next;
  1180. }
  1181. }
  1182. handle->shapes = static_cast<NSVGshape*>(std::calloc(1, sizeof(NSVGshape)));
  1183. handle->shapes->stroke.color = 0xff00ff00;
  1184. handle->shapes->stroke.type = NSVG_PAINT_COLOR;
  1185. handle->shapes->fill.color = 0xff000000;
  1186. handle->shapes->fill.type = NSVG_PAINT_COLOR;
  1187. return handle;
  1188. */
  1189. #ifndef HEADLESS
  1190. const size_t filenamelen = std::strlen(filename);
  1191. bool hasDarkMode = false;
  1192. bool hasLightMode = false;
  1193. NSVGimage* handleOrig;
  1194. NSVGimage* handleMOD = nullptr;
  1195. NSVGshape* shapesOrig;
  1196. NSVGshape* shapesMOD;
  1197. if (filenamelen < 18)
  1198. {
  1199. shapesOrig = shapesMOD = nullptr;
  1200. goto postparse;
  1201. }
  1202. #if 0
  1203. // Special case for GlueTheGiant
  1204. if (std::strstr(filename, "/GlueTheGiant/res/") != nullptr)
  1205. {
  1206. printf("special hack for glue\n");
  1207. if (std::strncmp(filename + (filenamelen - 13), "/BusDepot.svg", 13) == 0 ||
  1208. std::strncmp(filename + (filenamelen - 13), "/BusRoute.svg", 13) == 0 ||
  1209. std::strncmp(filename + (filenamelen - 13), "/EnterBus.svg", 13) == 0 ||
  1210. std::strncmp(filename + (filenamelen - 12), "/ExitBus.svg", 12) == 0 ||
  1211. std::strncmp(filename + (filenamelen - 11), "/GigBus.svg", 11) == 0 ||
  1212. std::strncmp(filename + (filenamelen - 17), "/MetroCityBus.svg", 17) == 0 ||
  1213. std::strncmp(filename + (filenamelen - 12), "/MiniBus.svg", 12) == 0 ||
  1214. std::strncmp(filename + (filenamelen - 9), "/Road.svg", 9) == 0 ||
  1215. std::strncmp(filename + (filenamelen - 14), "/SchoolBus.svg", 14) == 0)
  1216. {
  1217. const std::string nightfilename = std::string(filename).substr(0, filenamelen-4) + "_Night.svg";
  1218. hasDarkMode = true;
  1219. shapesOrig = shapesMOD = nullptr;
  1220. handleMOD = nsvgParseFromFile(nightfilename.c_str(), units, dpi);
  1221. printf("special hack for glue: %s -> %s\n", filename, nightfilename.c_str());
  1222. goto postparse;
  1223. }
  1224. }
  1225. #endif
  1226. for (size_t i = 0; i < sizeof(svgFilesToInvertForDarkMode)/sizeof(svgFilesToInvertForDarkMode[0]); ++i)
  1227. {
  1228. const char* const svgFileToInvert = svgFilesToInvertForDarkMode[i].filename;
  1229. const size_t filterlen = std::strlen(svgFileToInvert);
  1230. if (filenamelen < filterlen)
  1231. continue;
  1232. if (std::strncmp(filename + (filenamelen-filterlen), svgFileToInvert, filterlen) != 0)
  1233. continue;
  1234. const DarkMode mode = svgFilesToInvertForDarkMode[i].mode;
  1235. const char* const* const shapeIdsToIgnore = svgFilesToInvertForDarkMode[i].shapeIdsToIgnore;
  1236. const int shapeNumberToIgnore = svgFilesToInvertForDarkMode[i].shapeNumberToIgnore;
  1237. int shapeCounter = 0;
  1238. hasDarkMode = true;
  1239. handleMOD = nullptr;
  1240. shapesOrig = handle->shapes;
  1241. shapesMOD = nsvg__duplicateShapes(shapesOrig);
  1242. // shape paint inversion
  1243. for (NSVGshape* shape = shapesMOD; shape != nullptr; shape = shape->next, ++shapeCounter)
  1244. {
  1245. if (shapeNumberToIgnore == shapeCounter)
  1246. continue;
  1247. bool ignore = false;
  1248. for (size_t j = 0; j < 5 && shapeIdsToIgnore[j] != nullptr; ++j)
  1249. {
  1250. if (std::strcmp(shape->id, shapeIdsToIgnore[j]) == 0)
  1251. {
  1252. ignore = true;
  1253. break;
  1254. }
  1255. }
  1256. if (ignore)
  1257. continue;
  1258. if (invertPaintForDarkMode(mode, shape, shape->fill, svgFileToInvert))
  1259. invertPaintForDarkMode(mode, shape, shape->stroke, svgFileToInvert);
  1260. }
  1261. goto postparse;
  1262. }
  1263. for (size_t i = 0; i < sizeof(svgFilesToInvertForLightMode)/sizeof(svgFilesToInvertForLightMode[0]); ++i)
  1264. {
  1265. const char* const svgFileToInvert = svgFilesToInvertForLightMode[i].filename;
  1266. const size_t filenamelen = std::strlen(filename);
  1267. const size_t filterlen = std::strlen(svgFileToInvert);
  1268. if (filenamelen < filterlen)
  1269. continue;
  1270. if (std::strncmp(filename + (filenamelen-filterlen), svgFileToInvert, filterlen) != 0)
  1271. continue;
  1272. const LightMode mode = svgFilesToInvertForLightMode[i].mode;
  1273. hasLightMode = true;
  1274. handleMOD = nullptr;
  1275. shapesOrig = handle->shapes;
  1276. shapesMOD = nsvg__duplicateShapes(shapesOrig);
  1277. // shape paint inversion
  1278. for (NSVGshape* shape = shapesMOD; shape != nullptr; shape = shape->next)
  1279. {
  1280. if (invertPaintForLightMode(mode, shape, shape->fill))
  1281. invertPaintForLightMode(mode, shape, shape->stroke);
  1282. }
  1283. goto postparse;
  1284. }
  1285. // Special case for AmalgamatedHarmonics background color
  1286. if (handle->shapes != nullptr && handle->shapes->fill.color == 0xff000000)
  1287. if (std::strstr(filename, "/AmalgamatedHarmonics/") != nullptr)
  1288. handle->shapes->fill.color = 0xff191919;
  1289. postparse:
  1290. if (handleMOD != nullptr)
  1291. {
  1292. handleOrig = static_cast<NSVGimage*>(malloc(sizeof(NSVGimage)));
  1293. std::memcpy(handleOrig, handle, sizeof(NSVGimage));
  1294. }
  1295. else
  1296. {
  1297. handleOrig = nullptr;
  1298. }
  1299. if (hasDarkMode)
  1300. {
  1301. const ExtendedNSVGimage ext = { handle, handleOrig, handleMOD, shapesOrig, shapesMOD };
  1302. loadedDarkSVGs.push_back(ext);
  1303. if (rack::settings::preferDarkPanels)
  1304. {
  1305. if (shapesMOD != nullptr)
  1306. handle->shapes = shapesMOD;
  1307. else if (handleMOD != nullptr)
  1308. std::memcpy(handle, handleMOD, sizeof(NSVGimage));
  1309. }
  1310. }
  1311. if (hasLightMode)
  1312. {
  1313. const ExtendedNSVGimage ext = { handle, handleOrig, handleMOD, shapesOrig, shapesMOD };
  1314. loadedLightSVGs.push_back(ext);
  1315. if (!rack::settings::preferDarkPanels)
  1316. {
  1317. if (shapesMOD != nullptr)
  1318. handle->shapes = shapesMOD;
  1319. else if (handleMOD != nullptr)
  1320. std::memcpy(handle, handleMOD, sizeof(NSVGimage));
  1321. }
  1322. }
  1323. #endif // HEADLESS
  1324. return handle;
  1325. }
  1326. return nullptr;
  1327. }
  1328. void nsvgDeleteCardinal(NSVGimage* const handle)
  1329. {
  1330. #ifndef HEADLESS
  1331. for (auto it = loadedDarkSVGs.begin(), end = loadedDarkSVGs.end(); it != end; ++it)
  1332. {
  1333. ExtendedNSVGimage& ext(*it);
  1334. if (ext.handle != handle)
  1335. continue;
  1336. deleteExtendedNSVGimage(ext);
  1337. loadedDarkSVGs.erase(it);
  1338. break;
  1339. }
  1340. for (auto it = loadedLightSVGs.begin(), end = loadedLightSVGs.end(); it != end; ++it)
  1341. {
  1342. ExtendedNSVGimage& ext(*it);
  1343. if (ext.handle != handle)
  1344. continue;
  1345. deleteExtendedNSVGimage(ext);
  1346. loadedLightSVGs.erase(it);
  1347. break;
  1348. }
  1349. #endif
  1350. nsvgDelete(handle);
  1351. }
  1352. namespace rack {
  1353. void switchDarkMode(const bool darkMode)
  1354. {
  1355. #ifndef HEADLESS
  1356. settings::preferDarkPanels = darkMode;
  1357. settings::uiTheme = darkMode ? "dark" : "light";
  1358. ui::refreshTheme();
  1359. plugin::updateStaticPluginsDarkMode();
  1360. for (ExtendedNSVGimage& ext : loadedDarkSVGs)
  1361. {
  1362. if (ext.shapesMOD != nullptr)
  1363. ext.handle->shapes = darkMode ? ext.shapesMOD : ext.shapesOrig;
  1364. else if (ext.handleMOD != nullptr)
  1365. std::memcpy(ext.handle, darkMode ? ext.handleMOD : ext.handleOrig, sizeof(NSVGimage));
  1366. }
  1367. for (ExtendedNSVGimage& ext : loadedLightSVGs)
  1368. {
  1369. if (ext.shapesMOD != nullptr)
  1370. ext.handle->shapes = !darkMode ? ext.shapesMOD : ext.shapesOrig;
  1371. else if (ext.handleMOD != nullptr)
  1372. std::memcpy(ext.handle, !darkMode ? ext.handleMOD : ext.handleOrig, sizeof(NSVGimage));
  1373. }
  1374. #endif
  1375. }
  1376. namespace asset {
  1377. void destroy() {
  1378. #ifndef HEADLESS
  1379. for (auto it = loadedDarkSVGs.begin(), end = loadedDarkSVGs.end(); it != end; ++it)
  1380. {
  1381. ExtendedNSVGimage& ext(*it);
  1382. deleteExtendedNSVGimage(ext);
  1383. }
  1384. for (auto it = loadedLightSVGs.begin(), end = loadedLightSVGs.end(); it != end; ++it)
  1385. {
  1386. ExtendedNSVGimage& ext(*it);
  1387. deleteExtendedNSVGimage(ext);
  1388. }
  1389. loadedDarkSVGs.clear();
  1390. loadedLightSVGs.clear();
  1391. #endif
  1392. }
  1393. }
  1394. }