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.

1521 lines
57KB

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