1
0
mirror of https://github.com/DarthAffe/RGB.NET.git synced 2025-12-12 17:48:31 +00:00

Compare commits

...

607 Commits

Author SHA1 Message Date
b0acc61618
Merge pull request #430 from DarthAffe/Development 2025-07-17 21:39:16 +02:00
13f3dde185
Merge pull request #429 from DarthAffe/logitechpid
Added detection for logitech G515
2025-07-17 21:35:38 +02:00
845efb6929 Added detection for logitech G515 2025-07-17 21:33:15 +02:00
Diogo Trindade
02d695c4dd
Merge pull request #427 from diogotr7/feature/wooting-grpc
Add Wooting Grpc Rgb sdk client
2025-06-18 22:31:45 +01:00
Diogo Trindade
35a4033521
connect on ipv4 only
speeds up first connection significantly
2025-06-18 22:23:23 +01:00
Diogo Trindade
dc6715b236
use serial number hash as update trigger id 2025-06-18 19:45:22 +01:00
Diogo Trindade
34b04523de
make wootingcolor readonly, use int for array index 2025-06-18 19:31:00 +01:00
Diogo Trindade
a96b994e61
Fix warning 2025-06-18 19:05:33 +01:00
Diogo Trindade
f26b3c193a
Added gRPC sdk client 2025-06-18 19:01:20 +01:00
c0828c1d01
Merge pull request #426 from DarthAffe/Devices/Razer
Added PID for Razer Barracuda X Chroma
2025-05-28 20:14:12 +02:00
7c65b1d0bf Added PID for Razer Barracuda X Chroma
#425
2025-05-28 20:10:33 +02:00
826a5104de
Merge pull request #424 from kyriog/patch-1
Adding Razer Huntsman V3 Pro
2025-05-22 23:57:28 +02:00
Kyriog
80b2365c31
Adding Razer Huntsman V3 Pro 2025-05-12 19:42:22 +02:00
8756ec98f3 Added Razer BlackWidow V4 Pro 75% (#423) 2025-04-27 16:38:03 +02:00
d5ce5fcee7
Merge pull request #422 from jonilala796/SteelSeries-QCK-Prism-Cloth-3XL
Added support for SteelSeries QCK Prism Cloth 3XL
2025-03-11 23:33:35 +01:00
jonilala796
6a47887c18 Added support for SteelSeries QCK Prism Cloth 3XL 2025-03-11 20:21:14 +01:00
eb4ea71d01
Merge pull request #421 from jonilala796/Razer-Kraken-V3-HyperSense
Added support for Kraken V3 HyperSense headset
2025-03-10 22:08:41 +01:00
jonilala796
b0f05ab399 Added support for Kraken V3 HyperSense headset 2025-03-08 21:53:46 +01:00
e1475859c6
Merge pull request #420 from DarthAffe/feature/more-80he-fixes
Fix more 80HE layout
2025-02-21 01:56:42 +01:00
Diogo Trindade
9499b31c13
Fix more 80HE layout 2025-02-20 18:28:07 +00:00
7fabc71b28
Merge pull request #419 from jonilala796/master
Add support for Corsair RX and LX Fans
2025-02-16 14:04:41 +01:00
jonilala796
01bad00d8a simplify LX Fan logic in CorsairDeviceProvider 2025-02-08 21:52:28 +01:00
jonilala796
1deb1b9f2f Add support for RX Fan naming 2025-02-08 21:37:24 +01:00
jonilala796
66843a21e5 Add workaround for LX Fans with invalid ChannelDeviceType 2025-02-08 21:23:33 +01:00
4e575e8d3d
Merge pull request #418 from DarthAffe/Development
v3
2025-02-08 16:43:21 +01:00
f8e4cc6b49
Merge pull request #417 from DarthAffe/LanguageUsage
Language usage
2025-02-08 12:20:26 +01:00
aae509b275 More code issues fixed 2025-02-08 12:13:14 +01:00
5633f82b3b Changed locks to use the new Lock-type for .net9 2025-02-08 12:08:12 +01:00
47770c00b8 Used new collection-initialization 2025-02-08 12:07:48 +01:00
654a7624cd
Merge pull request #412 from DarthAffe/net9
Net9
2025-02-06 20:27:19 +01:00
f9fefe4667
Merge pull request #416 from DarthAffe/SteelSeriesPids
SteelSeries pids
2025-02-06 20:23:03 +01:00
f40190da01 Updated dependencies 2025-02-06 20:22:36 +01:00
f34033a3fe
Merge pull request #410 from harrisck/patch-1
Update SteelSeriesDeviceProvider.cs for Apex Pro TKL Wireless Gen3
2025-02-06 20:14:58 +01:00
e151b3f194
Merge branch 'Development' into patch-1 2025-02-06 20:14:42 +01:00
899fc697e4
Merge pull request #413 from DarthAffe/LogitechPids
Added logitech G915 X (TKL)
2025-02-06 20:13:09 +01:00
d97293ec87
Merge pull request #414 from logicallysynced/device-additions
Added additional Razer & SteelSeries devices
2025-02-06 20:12:54 +01:00
12a353574a Added SteelSeries Arena 9 2025-02-06 20:07:06 +01:00
Danielle
77f55d2eb3 Added additional Razer & SteelSeries devices
Added device references for:
* Razer BlackWidow V4 X
* Razer Cobra
* SteelSeries Apex Pro 3
* SteelSeries Aerox 9 Wireless
2025-01-18 10:33:15 +11:00
9945a5f7cb Updated build-scripts to net9 2025-01-14 22:16:03 +01:00
ed98714439 Added logitech G915 X (TKL) 2025-01-14 22:12:54 +01:00
39da0fd59c Updated dependencies 2025-01-03 15:48:07 +01:00
1d47894dac Added .net9 build target 2025-01-03 15:45:07 +01:00
197be1f986
Merge pull request #408 from Nick026/Development
Added Razer Chroma Wireless ARGB Controller support
2024-12-30 02:05:27 +01:00
harrisck
a2a25bc40a
Update SteelSeriesDeviceProvider.cs for Apex Pro TKL Wireless Gen3
Added Device ID for Apex Pro TKL Wireless Gen3
2024-12-29 20:46:57 +00:00
505e5c6d80
Merge pull request #409 from DarthAffe/SteelSeriesMapping
Fixed mapping for StellSeries Apex 3
2024-12-03 09:54:31 +01:00
f8a530e313 Fixed mapping for StellSeries Apex 3 2024-12-01 21:02:35 +01:00
Nick S.
aa7bfd3976
Added Razer Chroma Wireless ARGB Controller support 2024-11-29 15:56:33 +01:00
71f1115c31
Merge pull request #403 from stonstad/Contributions
Remove CustomUpdateData heap allocation in OnUpdate hot path.
2024-11-03 15:44:03 +01:00
Shaun Tonstad
da51871e04
Update RGB.NET.Core/Update/CustomUpdateData.cs
Co-authored-by: DarthAffe <darthaffe@wyrez.org>
2024-10-29 20:19:29 -05:00
a26eaf9a9a
Merge pull request #335 from Aytackydln/asus-remove-system-management
Asus: Remove System.Management
2024-10-20 11:26:50 +02:00
54c09a1d6f
Fixed default name for Asus Keyboards 2024-10-20 11:23:23 +02:00
a0198e3991
Merge pull request #405 from DarthAffe/LogitechMultipleDevicesFix
Fixed crash in logitech device provider, when multiple lightspeed dev…
2024-10-20 11:19:08 +02:00
f2491038a3 Fixed crash in logitech device provider, when multiple lightspeed devices with the same usage are connected 2024-10-20 11:06:40 +02:00
5c864a03e5
Merge pull request #404 from diogotr7/feature/80he-layout-fixes
Fixed incorrect 80he international keys
2024-10-17 20:51:27 +02:00
Diogo Trindade
7cfe08921e
Fixed incorrect 80he international keys 2024-10-17 12:05:54 +01:00
Shaun Tonstad
c34435b958 Remove CustomUpdateData heap allocation in OnUpdate hot path. Added CustomUpdateData.Empty property.
Remove CustomUpdateData heap allocation in OnUpdate hot path. Added CustomUpdateData.Empty property.
2024-10-12 13:01:53 -05:00
2bf081d16e
Merge pull request #400 from DarthAffe/Devices/Razer
Added pid for Razer Firefly v2 Pro
2024-09-21 22:53:38 +02:00
466d136089 Added pid for Razer Firefly v2 Pro 2024-09-20 09:29:03 +02:00
13a5a6162d
Merge pull request #398 from diogotr7/feature/openrgb-fix
Update OpenRGB.NET
2024-07-28 19:39:26 +02:00
Diogo Trindade
54b4eac1f3
Update OpenRGB.NET 2024-07-28 17:17:08 +01:00
9346948bfb
Merge pull request #397 from DarthAffe/HPPH
Updated build-files
2024-07-22 00:04:53 +02:00
9a997694a5 Updated build-files 2024-07-22 00:01:50 +02:00
1396e978db Fixed doc-comment mistake 2024-07-22 00:00:28 +02:00
681adcb1cb
Merge pull request #396 from DarthAffe/HPPH
Added HPPH Image texture in presets; Removed old .NET versions
2024-07-21 23:56:58 +02:00
cfbbdc6069 Changed ImageTexture Image to be public 2024-07-21 23:52:06 +02:00
0d41314f7e
Merge pull request #395 from DarthAffe/SmallPerformanceThings
(MAJOR) Removed all in paramters as their performance impact is too h…
2024-07-21 23:47:32 +02:00
b1cf26b1e6 Added support for HPPH-images in presets 2024-07-21 23:46:13 +02:00
4c3875e561 Fixed a doc comment 2024-07-21 23:45:23 +02:00
3e53b51bac Removed support for old .NETs 2024-07-21 23:44:59 +02:00
fdfd35eff8
Merge pull request #394 from diogotr7/feature/wooting-80he
Add Wooting 80HE support
2024-07-21 23:12:22 +02:00
c39849949a (MAJOR) Removed all in paramters as their performance impact is too hard to predict 2024-07-21 23:10:56 +02:00
Diogo Trindade
d207fdf495
Add Wooting 80HE support 2024-07-20 23:09:09 +01:00
4e80764015
Merge pull request #393 from roxaskeyheart/patch-4
Added Razer Leviathan V2 X Speakers & BlackWidow V4 Mini HyperSpeed
2024-07-07 14:07:49 +02:00
9de1233c93
Update RGB.NET.Devices.Razer/RazerDeviceProvider.cs 2024-07-07 12:03:39 +00:00
Danielle
976ee6d007
Added BlackWidow V4 Mini HyperSpeed 2024-07-07 19:15:55 +10:00
Danielle
9f0c3be7bb
Added Razer Leviathan V2 X Speakers 2024-07-07 19:08:17 +10:00
Aytackydln
5e895f5806 Merge remote-tracking branch 'refs/remotes/origin/Development' into asus-remove-system-management 2024-07-02 17:31:41 +02:00
41a1d5d97b
Merge pull request #391 from DarthAffe/SmallPerformanceThings
Small performance things
2024-06-22 19:52:51 +02:00
6f0711564c Removed unnecessary in GetByteValueFromPercentage 2024-06-22 19:49:38 +02:00
f21acbd767 Fixed potential performance issue with for-loops when Spans are passed by reference 2024-06-22 19:48:52 +02:00
da2c2a678b
Merge pull request #390 from DarthAffe/RazerKeyboardMapping
Added media-keys and underglow-leds to razer-mapping; Correctly named…
2024-06-22 19:40:22 +02:00
5a13fcef7f Added media-keys and underglow-leds to razer-mapping; Correctly named BlackWidow v4 Pro 2024-06-22 19:30:47 +02:00
bc7c2374f9
Merge pull request #388 from DarthAffe/DeviceDetection
Added detection for SteelSeries Apex 3
2024-06-10 19:48:12 +02:00
ed7ff76210 Added detection for SteelSeries Apex 3 2024-06-10 19:46:06 +02:00
f4cf5c3286
Merge pull request #386 from DarthAffe/DeviceDetection
Added detection for a razer and a stellseries device
2024-05-27 22:42:19 +02:00
83d1b2472b Added detection for a razer and a stellseries device 2024-05-27 22:31:58 +02:00
1b9531a86f
Merge pull request #384 from DarthAffe/Development
Updated workflows
2024-04-11 23:46:07 +02:00
5f71ffdf1f
Merge pull request #383 from DarthAffe/WorkflowImprovements
Workflow improvements
2024-04-11 23:43:17 +02:00
835987155c
Removed semantiv-versioning from release 2024-04-11 23:30:19 +02:00
31db13cb3d
Removed semantic versioning action in ci 2024-04-11 23:24:24 +02:00
51373e4441 (MINOR) Merge pull request #382 from DarthAffe/Fix/CorsairCallback
Fix/corsair callback
2024-04-10 15:31:55 +02:00
8f7e24ba33 Fixed potential callback collection in CorsairDeviceProvider 2024-03-19 22:03:55 +01:00
c0adbad599
Update release.yml 2024-03-09 12:15:21 +01:00
4773e0df74
Merge pull request #381 from DarthAffe/Development
v2.1
2024-03-09 12:11:38 +01:00
29e68ae8fe
Update release.yml 2024-03-09 00:44:57 +01:00
5aac20e982
Update ci.yml 2024-03-09 00:44:36 +01:00
d1e71ef827
Update ci.yml 2024-03-08 23:46:26 +01:00
08d526a7a4
Update ci.yml 2024-03-08 23:44:18 +01:00
acd4462b2a
Update ci.yml 2024-03-08 23:38:45 +01:00
e58d26d58d
Update ci.yml 2024-03-08 23:34:47 +01:00
417b9be3f9
Merge pull request #380 from DarthAffe/Actions
(MINOR) Updated workflows and bumped version for release
2024-03-08 21:47:22 +01:00
ced13e9ec2 (MINOR) Updated workflows and bumped version for release 2024-03-08 20:50:58 +01:00
4f0f25e34e
Merge pull request #379 from DarthAffe/RazerDebugDevices
Razer: Load Debug Device Properties for Each Device Type
2024-03-08 20:38:01 +01:00
58ed8bb2f1
Merge pull request #378 from diogotr7/feature/msi-pid
Add extra MSI pid to steelseries provider
2024-03-08 20:35:40 +01:00
Aytaç Kayadelen
d82eebd769 Razer: Load Debug Device Properties for Each Device Type 2024-03-08 20:28:09 +01:00
Diogo Trindade
3ca782a6a7
Add extra MSI pid to steelseries provider 2024-03-02 22:16:07 +00:00
a4bd797912
Merge pull request #375 from Aytackydln/logi-shutdown-on-dispose
Call LogiLedShutdown on Logitech provider dispose
2024-02-12 14:57:37 +01:00
Aytaç Kayadelen
b56a50ab08 call shutdown on Logitech provider dispose 2024-02-11 18:26:10 +01:00
50e20a7bdd
Merge pull request #374 from Aytackydln/num-lock-scroll-indicators
added Num/Caps/Scroll lock indicator mappings to OpenRGB
2024-01-28 22:20:43 +01:00
Aytaç Kayadelen
4959c6e8ad added Num/Caps/Scroll lock indicator mappings to OpenRGB 2024-01-27 22:19:46 +01:00
ef980f754c
Merge pull request #373 from DarthAffe/SDK/Wled
Changed WLED-devices to flush and reduced heartbeat timer
2024-01-18 23:41:52 +01:00
1df23a6b36 Changed WLED-devices to flush and reduced heartbeat timer 2024-01-18 23:35:19 +01:00
16b2877dce
Merge pull request #371 from DarthAffe/SDK/Wled
Added Provider for WLED-Devices
2024-01-18 20:40:07 +01:00
657bca29b0
Merge pull request #372 from DarthAffe/SmallFixes
Small fixes
2024-01-18 20:39:42 +01:00
6ba7e44e3c Added mising dispose in E131UpdateQueue 2024-01-17 23:39:39 +01:00
9004c50d78 Disabled resharper warning about missspelling of MSI 2024-01-17 23:39:06 +01:00
b2848cfbaa Fixed small mistake in WLED readme 2024-01-17 23:36:04 +01:00
23bd1119f2 Added helper to auto discover WLED devices to the readme 2024-01-17 23:34:43 +01:00
67672be54c Added Provider for WLED-Devices 2024-01-17 23:02:02 +01:00
3400edcf93
Merge pull request #370 from Myp3a/GE78LedMapping
Added MSI Raider GE78HX LED mapping
2024-01-17 10:25:33 +01:00
Myp3a
1fe8cab0a4 Added MSI GE78HX LED mapping 2024-01-16 17:42:02 +03:00
95f162de2d
Merge pull request #368 from DarthAffe/DarthAffe-patch-1
Update ci.yml
2024-01-09 23:45:30 +01:00
f07d228408
Update ci.yml 2024-01-09 22:44:33 +00:00
1312566eb0
Merge pull request #367 from DarthAffe/Devices/Razer
Added PID for Razer Naga Classic Edition
2024-01-09 23:18:06 +01:00
9aee9a1640 Renamed Razer Naga Classic Edition To Naga Classic 2024-01-09 22:57:28 +01:00
c89bbfea96 Added PID for Razer Naga Classic Edition 2024-01-09 22:56:13 +01:00
af43c0ae87
Merge pull request #365 from DarthAffe/Devices/Novation
Added Novation Launchpad Mini MK3
2024-01-04 20:12:24 +01:00
7898c50eed Fixed unexpected name of the Launchpad Mini MK3 2024-01-04 18:10:31 +01:00
505e7481f7 Small Novation-detection refactoring 2024-01-04 18:10:20 +01:00
4f478879c2 Added Novation Launchpad Mini MK3 2024-01-04 17:29:59 +01:00
Aytaç Kayadelen
8b1c5927d3 Merge branch 'Development' of https://github.com/DarthAffe/RGB.NET into asus-remove-system-management 2024-01-01 22:12:05 +01:00
5a104eaf24
Merge pull request #364 from DarthAffe/Development
v2.0.0
2024-01-01 00:00:50 +01:00
79f71bab29
Merge pull request #362 from DarthAffe/Core/CodeStyling
Applied some C#12 stuff
2023-12-31 16:28:51 +01:00
aeab93001f More code styling fixes 2023-12-27 15:48:20 +01:00
2796acd775
Merge pull request #363 from DarthAffe/Ci-Tags
Added tag-creation to ci workflow
2023-12-23 00:15:25 +01:00
cc2c7bc973
Added tag-creation to ci workflow 2023-12-22 23:14:55 +00:00
d85f1559b3 Applied some C#12 stuff 2023-12-22 20:56:56 +01:00
0444730aca
Merge pull request #361 from DarthAffe/SDK/Corsair
Added GameController as Core device type; Updated Corsair SDK to 4.0.84
2023-12-22 20:18:34 +01:00
8aaf602cdd Added GameController as Core device type; Updated Corsair SDK to 4.0.84 2023-12-22 20:16:14 +01:00
Aytaç Kayadelen
1bd5b54892 pull 2023-12-06 22:19:43 +01:00
Aytaç Kayadelen
c11965eb3a Merge branch 'Development' of https://github.com/DarthAffe/RGB.NET into asus-remove-system-management 2023-12-06 20:48:53 +01:00
9575690efb
Merge pull request #359 from DarthAffe/Update/net8
Added .NET 8 build-target
2023-11-14 22:36:54 +01:00
a0c90029eb Added .NET 8 build-target 2023-11-14 22:34:13 +01:00
5592fd9923
Merge pull request #358 from DarthAffe/bugfix/layout-saving
Fix custom LED data type not being determined correctly
2023-11-01 21:14:44 +01:00
Robert
08fbb0e526 Fix custom LED data type not being determined correctly 2023-11-01 20:37:12 +01:00
99225f04fc
Merge pull request #357 from DarthAffe/LayoutFixes
Layout fixes/improvements
2023-11-01 20:16:23 +01:00
fda89e74c2
Merge pull request #355 from DarthAffe/feature/wooting-uwu
Wooting - Add uwu support
2023-11-01 20:15:50 +01:00
be252790d4 Added extension to save layouts 2023-10-31 22:14:05 +01:00
188de3c558 Fixed an error when parsing custom layout data if the xml-element contains an attribute 2023-10-31 21:54:58 +01:00
883d6cbea4 Added ToString to Scale 2023-10-31 21:51:40 +01:00
Diogo Trindade
fcf86ff9da Fix analog key coords 2023-10-18 14:11:44 +01:00
Diogo Trindade
7ad1e595a9 Added Missing LEDs 2023-10-17 19:29:40 +01:00
Diogo Trindade
69d320fca3 Ignore UwU non-RGB 2023-10-17 17:49:09 +01:00
Diogo Trindade
736d58c7a3 Wooting - Add uwu support 2023-10-17 14:20:04 +01:00
d454f94b73
Merge pull request #353 from DarthAffe/RazerPids
Added Razer Kraken Kitty V2 PID
2023-09-22 22:02:13 +02:00
012fdf62a4 Added PIDs for Blade 18, Naga V2 Pro (wired?) and Mouse Dock Pro 2023-09-22 21:55:53 +02:00
c1a4fee189 Added Razer Kraken Kitty V2 PID 2023-09-04 11:50:26 +02:00
2e0754f474
Merge pull request #352 from DarthAffe/IndicatorLedIds
Indicator led ids
2023-08-28 10:21:21 +02:00
5707a247ee Removed excessive spaces 2023-08-27 21:41:41 +02:00
72da2b6bdf Added LedIds for Indicator-LEDs (like NumLock) 2023-08-27 21:40:01 +02:00
60892e4e81
Merge pull request #350 from roxaskeyheart/razer-blackwidowv4-pid
Add additional Razer BlackWidow V4 PID
2023-08-23 10:14:05 +02:00
Danielle
c7abac5ecd
Update RazerDeviceProvider.cs
Added additional Razer Blackwidow V4 PID.
2023-08-23 14:22:38 +10:00
745364495d
Merge pull request #349 from DarthAffe/RazerPids
Razer pids
2023-08-23 00:57:58 +02:00
9d1188ab33 Fixed razer naming again 2023-08-23 00:57:19 +02:00
3461674e17 Typo in Keyboard name 2023-08-23 00:53:14 +02:00
5d3a3613e2 Added PID for Razer Black Widow V4 75% 2023-08-23 00:52:06 +02:00
aba0888bd9 Fixed typo in Core readme 2023-08-23 00:50:28 +02:00
3d9706ad0e
Merge pull request #348 from DarthAffe/SDK/Corsair
Fixed an exception when multiple invalid leds are present in custom c…
2023-08-19 19:58:25 +02:00
1f0b1b0774 Fixed an exception when multiple invalid leds are present in custom corsair devices 2023-08-17 23:22:33 +02:00
d070f0bbcb
Merge pull request #347 from DarthAffe/ProviderInit
Added missing provider-init-changes to CorsairLegacy
2023-08-16 21:59:13 +02:00
2c8bc72dc9 Added missing provider-init-changes to CorsairLegacy 2023-08-16 21:47:54 +02:00
4209905474
Merge pull request #336 from DarthAffe/ProviderInit
Improved DeviceProviders
2023-08-16 21:41:11 +02:00
Aytaç Kayadelen
cadf96634e Merge branch 'Development' of https://github.com/DarthAffe/RGB.NET into asus-remove-system-management 2023-08-07 09:30:40 +02:00
7415c6b4ef
Merge pull request #343 from DELUUXE/BladeLedMapping
Added Razer Blade 16 LED mapping
2023-07-26 01:01:43 +02:00
b4bbc6a343
Merge pull request #345 from DarthAffe/SDK/CorsairLegacy
Applied Led-Position-Fix from #331 to Corsair-Legacy-DeviceProvider
2023-07-25 22:19:41 +02:00
2a2053cc53 Applied Led-Position-Fix from #331 to Corsair-Legacy-DeviceProvider 2023-07-25 20:11:58 +02:00
DELUUXE
5483a7581d
Added custom led mapping for Razer Blade keyboards 2023-07-24 22:08:39 +02:00
fd6bfa05e3
Merge pull request #340 from DarthAffe/feature/layout-from-stream
Layout - Allow loading directly from a stream
2023-07-24 01:55:27 +02:00
d85d289909
Merge pull request #341 from DELUUXE/Development
Added Razer Blade 16 PID
2023-07-24 01:55:00 +02:00
89ab79aecd
Merge pull request #342 from DarthAffe/SDK/CorsairLegacy
Readded support for older CUE-SDKs
2023-07-24 01:54:35 +02:00
8faedd1e26 Fixed error in solution 2023-07-24 00:36:32 +02:00
6f91af9039 Readded support for older CUE-SDKs 2023-07-23 19:49:30 +02:00
DELUUXE
803acf2f99
Added Razer Blade 16 PID 2023-07-22 23:39:23 +02:00
Robert
ba99ecfbdc Layout - Allow loading directly from a stream 2023-07-09 13:43:54 +02:00
0609e49571 Added locks around device provider instance usages 2023-06-18 19:54:57 +02:00
fdc69fdac5 Implemented recommended dispose pattern for DeviceProviders 2023-06-15 23:03:24 +02:00
Aytaç Kayadelen
5cb5962c1e remove System.Management dependency 2023-06-14 22:41:07 +02:00
56d77dee35
Merge pull request #331 from diogotr7/feature/corsair-size
Corsair - Fixed corsair partial devices getting longer
2023-06-12 19:54:13 +02:00
88a0f0fa11
Merge pull request #334 from DarthAffe/razer-pids
Fixed duplicate Razer PID
2023-06-12 19:53:59 +02:00
Robert
2b203b2308 Fix duplicate 2023-06-12 19:48:52 +02:00
4fb1dca55d
Merge pull request #333 from DarthAffe/razer-pids
Added more Razer keyboard and mouse PIDs
2023-06-11 14:02:09 +02:00
Robert
bf711325cf Added more Razer keyboard and mouse PIDs 2023-06-11 10:19:27 +02:00
83bdfea2f3
Merge pull request #321 from DarthAffe/Core/DevicesChangedEvent
Core/devices changed event
2023-06-03 21:29:56 +02:00
d7fffb4213
Added comment to new corsai FixOffsetDeviceLayout-method 2023-06-03 19:28:15 +00:00
Diogo Trindade
ca3c9fc5d3 WIP - Fixed corsair partial devices getting longer
the first device has the correct size, all following ones are larger than intended. tested with corsaid lighting node core and sp120
2023-06-03 19:44:37 +01:00
a0bdd837e2
Merge pull request #329 from diogotr7/feature/update-openrgb-net
Update OpenRGB.NET nuget
2023-05-24 00:22:08 +02:00
Diogo Trindade
e547bae209 Update OpenRGB.NET nuget 2023-05-23 22:58:13 +01:00
e96e1d636f
Merge pull request #328 from DarthAffe/Steelseries/Devices
Added SteelSeries Aerox 5 Wireless #323
2023-05-21 11:57:12 +02:00
5dbcd9c81e Added SteelSeries Aerox 5 Wireless #323 2023-05-20 22:34:29 +02:00
79252d5b31
Merge pull request #319 from DragRedSim/asus-rog-strix-laptop
Asus - added mapping for ROG Strix G15 (2021) laptop
2023-05-20 22:00:05 +02:00
27018b34e3
Merge pull request #325 from roxaskeyheart/patch-3
Add more Razer Keyboard HIDs
2023-05-20 21:48:13 +02:00
0aca2d84b1
Apply suggestions from code review 2023-05-20 19:36:53 +00:00
020f249f9a
Merge pull request #322 from DarthAffe/NativeStuff
Native stuff
2023-05-20 21:27:41 +02:00
1532e31a33 Improved Stop of DeviceUpdateTrigger 2023-05-15 22:50:47 +02:00
b90c47076a Changed DevicesChangedEventArgs to only contain the device causing the change and an enum indicatin what happened 2023-05-15 22:45:28 +02:00
Danielle
d53801117b
Add more Razer Keyboard HIDs
Added extra Razer Keyboard device HIDs, sourced from https://github.com/openrazer/openrazer.
2023-05-11 19:44:37 +10:00
a196d2a0a4 Changed the calculation mode of the SolidColor-brush to absolute for performance reasons 2023-05-08 22:07:49 +02:00
73b7f1f24f Changed Bindable-Methods to not be virtual since there is not really a point in overriding them 2023-05-08 22:04:36 +02:00
124f76b382 Fixed comment 2023-05-08 22:03:41 +02:00
4684e29610 Changed DevicesChanged-event to provide a single device instead of a list 2023-05-08 21:47:51 +02:00
67f3625993 Changed device lists in the DevicesChanged-event to readonly 2023-05-08 21:30:52 +02:00
7e72d3199b Added DevicesChanged-event to device providers 2023-05-08 21:28:55 +02:00
acddfed2b1 Removed allocation when applying decorators 2023-05-08 20:47:47 +02:00
David Ross Smith
184a5823e8 Asus - added LED mapping for ROG Strix G15 (2021)
- Device is a laptop with RGB in the keyboard, as well as an LED strip
- LED mappings reuse those from the ROG Zephyrus Duo where appropriate
2023-05-02 21:55:36 +10:00
David Ross Smith
e9cd657eac Asus - Corrected unspecified device documentation
- Previous info was copy/pasted from headset values
2023-05-02 21:51:32 +10:00
David Ross Smith
0c8f48ea44 Asus - Modify device type enum to add new items
- USB keyboard with 5-zone lighting (also passed as a keyboard)
- Watercooler (uses generic lighting)

- Sourced from https://www.asus.com/microsite/aurareadydevportal/interface_aura_service_lib_1_1_i_aura_sdk.html
2023-05-02 21:50:47 +10:00
93cd8055a2 Simplified span-fixes in Samplers 2023-04-24 23:13:12 +02:00
4a0ae1a185
Merge pull request #318 from DarthAffe/RenderPerformance
Changed stride and DataPerPixel in the PixelTexture to be a property …
2023-04-23 18:05:56 +02:00
a2194849b6 Changed stride and DataPerPixel in the PixelTexture to be a property for consistency 2023-04-23 18:05:24 +02:00
346d502e0f
Merge pull request #317 from DarthAffe/RenderPerformance
Changed DataPerPixel and Stride to be protected in PixelTexture
2023-04-23 18:03:02 +02:00
9f8e64fbcb Changed DataPerPixel and Stride to be protected in PixelTexture 2023-04-23 18:02:11 +02:00
54f39f8023
Merge pull request #316 from DarthAffe/RenderPerformance
Render performance
2023-04-23 17:47:23 +02:00
928d5c2ef9 Added license link to SimplexNoise 2023-04-23 17:36:22 +02:00
586734b44a (MAJOR) Improvied sampling performance by removing the need to copy region data to a buffer first 2023-04-23 17:19:51 +02:00
af3989aa73 Added Test for the PixelTexture 2023-04-23 12:16:38 +02:00
f5f81e74d7
Merge pull request #315 from DarthAffe/Workflows
Removed .NET 5 artifacts from build
2023-04-19 22:15:48 +02:00
e8f168f64a Removed .NET 5 artifacts from build 2023-04-19 22:15:27 +02:00
25fef22218
Merge pull request #313 from DarthAffe/Test/PerformanceOptimization
Test/performance optimization
2023-04-19 22:08:45 +02:00
2020992249 Sealed a lot of classes that are not meant to be inherited 2023-04-19 21:19:05 +02:00
d9c244a044 Small fixes 2023-04-19 20:11:30 +02:00
ad75707645 Removed SkipLocalInit-Attributes - they're causing issues and are not worth the effort for now 2023-04-13 11:29:22 +02:00
260a820b80 Added SkipLocalsInitAttribute to Sample-Methods 2023-04-13 02:03:13 +02:00
4ee55c6725 Small fixes 2023-04-13 01:24:53 +02:00
70ccc4d575 Reduced some more allocations 2023-04-13 00:49:29 +02:00
0cf4f39ccf Reduced allocations when a ListLedGroup is used 2023-04-13 00:21:20 +02:00
764fcd1b1d (MAJOR) Improved update performance of devices 2023-04-12 22:25:24 +02:00
4216dacf4f Removed unnecessary contains check in AbstractReferenceCounting 2023-04-11 00:29:01 +02:00
f6433af4b5 Merge branch 'Development' into SomeFixes 2023-04-11 00:27:06 +02:00
82050b8d5a Fixed some code issues 2023-04-11 00:26:46 +02:00
02235a3f7f Fixed some code-issues 2023-04-11 00:15:27 +02:00
aaabbc6a8d (MAJOR): Removed support for .NET 5; Updated nugets; Removed the not even working distribution of the Asus-SDK from the project; Fixed a warning in the OpenRGBServerDefinition 2023-04-11 00:06:17 +02:00
6bed6906d4
Merge pull request #311 from DarthAffe/DisposeFix
Dispose fix
2023-04-10 19:38:37 +02:00
10183fb270 Added dispose-check to corsairs update queue 2023-04-09 16:36:27 +02:00
b7df7e4d02
Merge pull request #310 from DarthAffe/SDK/RazerPids
Added PID for Razer Ornata V3
2023-04-08 21:21:05 +02:00
818678fdf2 Added PID for Razer Ornata V3
fixes #309
2023-04-08 21:10:12 +02:00
5b514ff962 Moved reference check for UpdateQueue disposal to the caller to prevent issues with Dispose-overrides 2023-04-08 14:42:56 +02:00
d054d16c10 Added reference counting to update queues to prevent premature disposes when used in multiple devices 2023-04-08 00:53:15 +02:00
39cfcdb367
Merge pull request #308 from DarthAffe/SDK/RazerPids
Added Pid for Razer BlackWidow V4
2023-03-14 20:33:28 +01:00
12d6b90c74
Merge pull request #306 from diogotr7/feature/openrgb.net-2
OpenRgb - added segments
2023-03-14 20:31:03 +01:00
f089c61e9d
Merge pull request #307 from diogotr7/feature/wooting-macos
Wooting - Add macOS dylib
2023-03-14 20:30:44 +01:00
5eb39b1a07 Added Pid for Razer BlackWidow V4
Fixes #305
2023-03-14 20:27:59 +01:00
Diogo Trindade
1793d26166 OpenRgb - added segments 2023-03-09 15:33:50 +00:00
Diogo Trindade
b6342ea575 Wooting - Add macOS dylib 2023-03-09 14:43:23 +00:00
4675349621
Merge pull request #304 from DarthAffe/DeviceExceptionHandling
Device exception handling
2023-03-05 18:10:12 +01:00
4a9bbb64dc Added missing doc comment 2023-03-05 18:06:08 +01:00
30ccfdcd85 (MAJOR) Added success-indication to device updates and forced flushes after nonsuccessful ones.
Added exception handling the last missing queues.
2023-03-05 18:04:50 +01:00
1b4b92b44c
Merge pull request #303 from DarthAffe/DeviceExceptionHandling
Added exception-handling to all UpdateQueues
2023-03-05 16:38:32 +01:00
37e4954583 Added exception-handling to all UpdateQueues 2023-03-05 16:34:01 +01:00
6e313ea6a1
Merge pull request #301 from DarthAffe/SDK/Corsair
Added corsair session details
2023-02-12 16:28:15 +01:00
bb3b74b919 Removed debug output 2023-02-12 16:23:27 +01:00
cc652a08a6 Added corsair session details 2023-02-12 16:19:00 +01:00
d66e4d87d9
Merge pull request #300 from motabass/master
Add missing Logitech G915 TKL wireless PID
2023-02-12 15:55:58 +01:00
631c8d2047
Merge pull request #299 from roxaskeyheart/Development
Add definitions for Asus ROG Terminal
2023-02-12 15:55:49 +01:00
db2f16d371
Update RGB.NET.Devices.Asus/AsusDeviceProvider.cs 2023-02-12 13:48:01 +01:00
Markus Mohoritsch
b67294eb3c Added missing G915 TKL PID 2023-02-12 10:06:38 +01:00
Danielle
914bd8c0a1 Update AsusDeviceProvider.cs
Code cleanup
2023-02-12 12:45:30 +11:00
Danielle
df2f5620ee Update AsusDeviceProvider.cs 2023-02-12 12:44:39 +11:00
Danielle
3dc4f45f66 Merge branch 'Development' of https://github.com/DarthAffe/RGB.NET into Development 2023-02-12 11:41:31 +11:00
Danielle
38840ef6f4 Asus dev 2023-02-12 11:41:18 +11:00
df73551497
Merge pull request #298 from DarthAffe/Core/Optimizations
(MAJOR) Optimized surface-updating to reduce the amount of allocations
2023-02-11 23:03:49 +01:00
8431a8cb5e (MAJOR) Optimized surface-updating to reduce the amount of allocations 2023-02-11 22:36:59 +01:00
3ff357212b
Merge pull request #294 from roxaskeyheart/patch-2
Add some Razer device definitions
2023-02-11 16:31:46 +01:00
3ffa834568
Merge pull request #297 from DarthAffe/SDK/Corsair
Updated corsair SDK to iCUE SDK v4.0.48
2023-02-11 16:31:14 +01:00
2ff7a65519 Removed editorconfig 2023-02-10 19:25:18 +01:00
d6aed5c5a2 Fixed some code issues (dispose finalizers) 2023-02-10 19:23:48 +01:00
180b0e4538 Updated corsair SDK to iCUE SDK v4.0.48 2023-02-10 19:23:34 +01:00
Danielle
43ecc7374e
Update RazerDeviceProvider.cs 2023-02-06 13:47:34 +11:00
Danielle
3aba170d78
Update RazerDeviceProvider.cs
Added:
- Mouse: Razer Naga Pro
- Headset: Razer Nari
- Other: Razer Mouse Dock

Changed:
- Chroma Hardware Development Kit (HDK): This is a LED Controller
- Addressable RGB Controller: This is a LED Controller
2023-02-06 13:43:05 +11:00
287e3937cf
Merge pull request #292 from motabass/master
Add new PID to list of known Lightspeed Receivers
2023-02-05 16:24:12 +01:00
b779b58a9f
Merge pull request #290 from DarthAffe/Core/Optimizations
Reduced some allocations mostly due to boxing
2023-02-05 16:23:58 +01:00
Markus Mohoritsch
7c165f51af
Add new PID to list of known Lightspeed Receivers
Logitech is using a new PID 0xC547 for some of it's Lightspeed receivers.
2023-02-05 12:29:40 +01:00
d60d4833ca Reduced some allocations mostly due to boxing 2023-02-05 07:36:50 +01:00
be3f2bfa29
Merge pull request #287 from DarthAffe/Development
v1.0
2023-01-06 23:30:44 +01:00
b8b2343d6c
Merge pull request #286 from DarthAffe/ReadMe
ReadMe
2023-01-06 18:02:41 +01:00
5842922922 Removed unused Roccat-project 2023-01-06 17:31:16 +01:00
ecff7afec9 Improved format in readmes 2023-01-06 17:30:46 +01:00
89f4de41c7 Added ReadMe to NuGet 2023-01-06 17:26:18 +01:00
46fdd29735 Added readmes to projects 2023-01-06 15:51:31 +01:00
01e671ddeb Added defaults to openrgb device definition 2023-01-06 14:22:10 +01:00
0e093e1332
Merge pull request #284 from DarthAffe/Razer/NagaV2Pro
Added razer Naga V2 Pro
2023-01-03 20:00:50 +01:00
e6097cdfb2
Merge pull request #285 from diogotr7/feature/wooting-flush
Wooting - update for sdk v1.6
2023-01-03 20:00:39 +01:00
Diogo Trindade
5b5cf4417a Wooting - update for sdk v1.6 2023-01-03 16:08:00 +00:00
ab37569ca8 Added razer Naga V2 Pro 2022-12-27 15:00:00 +01:00
553f99af26 Typos 2022-12-26 00:50:22 +01:00
472214ab2d Updated readme 2022-12-26 00:49:12 +01:00
308edfdcb8
Merge pull request #273 from BigBrainAFK/DeviceSupport
Added Wooting 60percent physical layout
2022-12-24 01:07:00 +01:00
a7cde846b5
Merge pull request #283 from DarthAffe/Razer/BaseStationChroma
Changed Razer Base Station Chroma (V2) to be handled as a mousepad (r…
2022-12-16 23:25:29 +01:00
44cd29fd9e Changed Razer Base Station Chroma (V2) to be handled as a mousepad (reported through testing with debug devices) 2022-12-16 23:19:02 +01:00
ef83d721df
Merge pull request #282 from DarthAffe/SDK/SteelSeriesTenZoneDevices
Sdk/steel series ten zone devices
2022-12-15 01:09:05 +01:00
b3f9a206e9 Added steelseries rival 5 2022-12-14 23:56:30 +01:00
dccd44cef8 Added support for ten-zone-devices to steelseries provider 2022-12-14 23:56:15 +01:00
Tony Langhammer
86f0335696
Update RGB.NET.Devices.Wooting/Keyboard/WootingKeyboardLedMappings.cs
Co-authored-by: DarthAffe <darthaffe@wyrez.org>
2022-11-28 11:13:02 +01:00
e6a2fa592d
Merge pull request #278 from Cheerpipe/speaker_device_type
Add missing Speaker device type mapping
2022-11-23 19:42:34 +01:00
Cheerpipe
e6b5e5a77c Add missing Speaker device type mapping 2022-11-23 10:57:38 -03:00
5ba98d3dcd
Merge pull request #277 from DarthAffe/Core/IdGenerationFix
Fixed potential endless-loop in id generation
2022-11-14 19:23:43 +01:00
a2646ae43c Fixed potential endless-loop in id generation 2022-11-14 19:20:13 +01:00
fd3ca65207
Merge pull request #276 from DarthAffe/net7
Added .net 7 targets
2022-11-13 16:04:20 +01:00
8adeaab54d Changed ghactions to net7 2022-11-13 15:39:53 +01:00
3f1eb5ca59 Added .net 7 targets 2022-11-13 15:33:52 +01:00
8555d6b961
Merge pull request #275 from DarthAffe/DeviceHeartbeat
Device heartbeats
2022-11-06 02:12:00 +01:00
b12d0dc631 Changed SteelSeries devices to use the core heartbeat functionality 2022-11-05 21:11:41 +01:00
ca8bc67f03 Small refactoring 2022-11-05 21:11:17 +01:00
2c71196fce Fixed heartbeat timer check 2022-11-05 21:11:01 +01:00
15c056f11b Addded heartbeats to dmx devices 2022-11-05 21:07:11 +01:00
c6cfe35124 Added heartbeat functionality to device update trigger 2022-11-05 21:06:59 +01:00
4b5d076953
Merge pull request #274 from DarthAffe/feature/openrgb
Added OpenRGB device provider
2022-09-26 14:42:19 +02:00
d345de2c40 Applied code-formatting 2022-09-26 14:35:03 +02:00
Diogo Trindade
7954b876ef OpenRGB - Switch to file scoped namespaces 2022-09-25 15:37:43 +01:00
Diogo Trindade
2d203d31ab Added OpenRGB device provider 2022-09-25 15:25:20 +01:00
a9adf9763c
Merge pull request #271 from DarthAffe/NativeLibraryLoading
Native library loading
2022-09-25 15:44:56 +02:00
200631fbdb Streamlined usage of NativeLibrary; Changed all device providers to use function pointers when possible 2022-09-24 22:55:57 +02:00
08142b30b2
Merge pull request #270 from DarthAffe/Core/SamplePerformance
Added intrinsics to improve sampler-performance
2022-09-02 15:15:04 +02:00
7b591445b6 Added intrinsics to improve sampler-performance 2022-09-02 14:42:58 +02:00
4dc04286b7 Added linux-paths to wooting and changed the native part to use function pointers 2022-08-21 16:30:23 +02:00
b06d070848
Merge pull request #269 from diogotr7/wooting-multi-device
Added Wooting multi device support
2022-08-21 15:43:27 +02:00
Diogo Trindade
f3d63f09a9 Dispose of each keyboard properly 2022-08-09 12:52:23 +01:00
Diogo Trindade
819e6ef1df Wooting - Added Multi device support 2022-08-09 12:35:33 +01:00
f17c18e5ec
Merge pull request #267 from DarthAffe/DeviceSupport
Device support
2022-07-17 13:44:57 +02:00
62c6ad0717
Merge branch 'Development' into DeviceSupport 2022-07-17 13:37:13 +02:00
526a4b3411
Merge pull request #268 from DarthAffe/SDK/SmallFixAndCorsairUpdate
Sdk/small fix and corsair update
2022-07-17 13:34:57 +02:00
3c876e5518
Merge pull request #266 from DarthAffe/Core/Improvements
Core/improvements
2022-07-17 13:33:58 +02:00
c38fee16fa Added corsair Touchbar-Device 2022-07-17 13:23:08 +02:00
293ddac47b Updated corsair for SDK 3.0.464 2022-07-17 12:05:34 +02:00
5b25b10068 Fixed tick-resolution issues on linux 2022-07-17 11:53:08 +02:00
105f996729 Fixed code issue 2022-07-17 11:52:42 +02:00
10ec33c938 Added some optimizations and tests for Colors 2022-07-17 11:52:29 +02:00
abbab09289 Added MSI Notebook through steelseries SDK 2022-07-17 11:47:11 +02:00
BigBrainAFK
481225cdc1
Fix mapping for mode Led 2022-07-02 16:48:56 +02:00
BigBrainAFK
684b59a32d
Added Wooting 60percent physical layout 2022-06-29 21:32:15 +02:00
258a0cca16 Added detection for novation launchpad pro and custom firmware (not all leds are currently supported) 2022-06-05 15:15:58 +02:00
349e8b9ec3 Added Razer Huntsman V2 (non analog) 2022-05-17 21:00:06 +02:00
ead342388b
Merge pull request #261 from DarthAffe/Core/Timers
Changed update-triggers to allow the usage of high resolution timers …
2022-05-07 16:10:37 +02:00
0696d91a71
Merge pull request #262 from DarthAffe/SDK/RazerPids
Added Razer Basilisk v3 PID
2022-05-07 16:10:24 +02:00
d1dae22133 Added Razer Basilisk v3 PID 2022-05-07 16:09:43 +02:00
d555406722 Removed high resolution timer counter 2022-05-07 16:00:23 +02:00
02d6f4e53e Fixed typo 2022-05-07 15:55:54 +02:00
39b511b8c5 Added missing class-doc 2022-05-07 00:20:12 +02:00
f02d4564fa Changed update-triggers to allow the usage of high resolution timers to improve accuracy (enabled by default) 2022-05-07 00:18:52 +02:00
0decf0b9d3 Updated corsair model-names to reflect the sdk-changes in 3.0.460 2022-04-15 16:58:50 +02:00
d484030821 Fixed novation devices not always beeing detected correctly 2022-04-15 16:42:59 +02:00
aa67a606a5
Merge pull request #260 from DarthAffe/NativePathFix
Fixed EnviromentVariables in NativePaths for all device providers
2022-04-01 01:13:58 +02:00
b3145a70d9 Fixed EnviromentVariables in NativePaths for all device providers 2022-03-31 22:23:22 +02:00
437ec50345
Merge pull request #259 from DarthAffe/Corsair/CustomDevicesFix
Fixed custom device creation for corsair
2022-03-31 21:50:48 +02:00
ea44c0d203 Fixed custom device creation for corsair 2022-03-31 21:31:13 +02:00
5e904eb748
Merge pull request #258 from DarthAffe/RazerMouseIdFix
Added ids for all locations of the razer mouse-id-grid
2022-02-22 21:18:16 +01:00
3ef1fa4cec
Merge pull request #257 from DarthAffe/SteelSeriesNonUsBackslashFix
Added custom zone for steelseries non-us-backslash
2022-02-22 21:18:04 +01:00
35cf0a3277
Merge pull request #254 from roxaskeyheart/Development
Fix Razer keyboard LED mappings
2022-02-22 21:09:52 +01:00
57c8ebef06
Merge pull request #251 from DarthAffe/CorsairSDKPaths
Added some more default SDK-names for Corsair
2022-02-22 21:08:16 +01:00
a6bd8cc293 Added ids for all locations of the razer mouse-id-grid 2022-02-22 21:06:25 +01:00
d190d356ac Added custom zone for steelseries non-us-backslash 2022-02-22 20:58:44 +01:00
61e5f78351
Fixed wrong sorting of prerelease versions
As recommended in a NugetGallery issue about the same problem
2022-01-17 01:05:11 +01:00
877129ab86
Merge pull request #255 from DarthAffe/Core/Fixed
Core/fixes
2022-01-16 15:12:56 +01:00
87a6bc2c3f Fixed alpha-component beeing ignored for the first ledgroup rendered 2022-01-16 14:21:04 +01:00
60a3c20732 Fixed small texture not beeing rendered at big rectangles 2022-01-16 14:20:32 +01:00
Danielle (Roxas)
a5c6dbbeb8
Update RazerDeviceProvider.cs 2022-01-14 19:30:54 +11:00
Danielle (Roxas)
c28099a145
Update LedMappings.cs 2022-01-14 19:24:10 +11:00
Danielle (Roxas)
575ba2a611
Update LedMappings.cs 2022-01-14 19:21:37 +11:00
Danielle (Roxas)
6132683855
Remove definitions for Razer Laptop and Blackwidow V3 2022-01-14 19:20:53 +11:00
2d07d85330
Merge pull request #250 from DarthAffe/Nuget
Nuget fixes
2022-01-05 15:17:53 +01:00
8ae65145fe Bumped copyright to 2022 2022-01-05 15:13:11 +01:00
9e67b292e1 Merge branch 'SmallFixes' into Nuget 2022-01-05 15:11:01 +01:00
b36c32a0dc Fixed Asus not packing Aura-Lib for .NET 5 2022-01-05 15:06:46 +01:00
b0dfbb2c74 Fixed project files to correctly show icon and license 2021-12-27 21:25:32 +01:00
f5899ffb6d Small code issue fix 2021-12-27 21:25:18 +01:00
abe9c2a600 Fixed prerelease version naming 2021-12-23 20:01:56 +01:00
4c6313961b (MAJOR) Final fixes for build-scripts 2021-12-23 19:40:44 +01:00
ec27f18d24
More CI-build fixes 2021-12-23 18:41:57 +01:00
ab098991c6
Fixed nuget push again 2021-12-23 18:30:43 +01:00
2741e45c33
Hopefully fixed nuget push in CI build 2021-12-23 18:24:29 +01:00
77acf01a12
Merge pull request #249 from DarthAffe/CI
Added build-scripts
2021-12-23 17:49:50 +01:00
ab04e85984 Added build-scripts 2021-12-23 17:44:57 +01:00
1a3ad689d8
Merge pull request #248 from DarthAffe/PicoPi
Added reset method and moved HID-packet fragmentation into the SDK
2021-12-15 21:58:31 +01:00
0b561a40bd Added reset method and moved HID-packet fragmentation into the SDK 2021-12-15 21:55:55 +01:00
2fceed6eaf Added some more default SDK-names for Corsair 2021-12-05 16:28:30 +01:00
3e53e5711f
Merge pull request #243 from F-Lehmann/SDK/Logitech
Added Powerplay to PerDeviceDeviceDefinitions
2021-11-26 23:18:00 +01:00
10b092ea91 Removed per-zone definition for POWERPLAY mousepad 2021-11-26 23:15:18 +01:00
4708b0eba3 Merge 2021-11-26 23:13:01 +01:00
e07b580bf9
Merge pull request #247 from DarthAffe/SDK/Logitech
Sdk/logitech
2021-11-26 22:57:56 +01:00
Diogo Trindade
1f3cef94c8 Logitech - Added retry mechanism to Lightspeed detector 2021-11-26 17:28:16 +00:00
7f7b4957f6 Fixed wrong debugtype in core 2021-11-24 21:20:46 +01:00
35d61e1496 Merge 2 2021-11-23 23:23:05 +01:00
02c4be2fa4 Merge 2021-11-23 23:22:54 +01:00
bf7b80d85e
Merge pull request #246 from DarthAffe/MultiTarget
Multi target
2021-11-13 23:29:27 +01:00
f6102bcf43 Fixed weird line brakes 2021-11-13 23:29:02 +01:00
eccf27f359 Fixed some code issues 2021-11-13 23:24:57 +01:00
bee329ff4f Added .NET 5 as second build target since it still compiles fine 2021-11-13 23:22:06 +01:00
4afa46f25d
Merge pull request #245 from DarthAffe/Core/CustomUpdateData
Improved custom update data to be easier to use
2021-11-13 23:02:29 +01:00
9a5fe75b60 Improved custom update data to be easier to use 2021-11-13 19:53:13 +01:00
dc34535cea
Merge pull request #244 from DarthAffe/NET6
Net6
2021-11-13 18:45:46 +01:00
71981a5cf3 Merge 2 2021-11-13 18:00:48 +01:00
f69560224d Merge 2021-11-13 17:57:09 +01:00
30624035f1 Changed all namespaces to file-scope 2021-11-13 17:35:08 +01:00
a0c6ccebea Fixed some code issues 2021-11-13 00:56:52 +01:00
07fde6e31b Updated to .NET 6 2021-11-13 00:54:30 +01:00
Felix Lehmann
2d73d79c0b Added Powerplay to PerDeviceDeviceDefinitions 2021-10-24 00:08:06 +02:00
Diogo Trindade
2b64773c47
Merge pull request #242 from diogotr7/SDK/razer-mouse-id
Added device definitions
2021-10-18 21:39:15 +01:00
edc4094e21
Merge pull request #239 from diogotr7/SDK/Wooting
Added wooting layout detection
2021-10-18 20:59:11 +02:00
Diogo Trindade
8280b99f0c Fixed steelseries device type 2021-10-13 17:50:17 +01:00
Diogo Trindade
fb9c98269f Added one more Pid 2021-10-13 17:46:45 +01:00
Diogo Trindade
7b52e9e3ee Added more device definitions 2021-10-13 17:25:39 +01:00
Diogo Trindade
39e3a1d11b Razer - Added basilisk V2 PID 2021-10-13 17:16:40 +01:00
11c31d07df
Merge pull request #238 from diogotr7/logitech-wireless-detection
Logitech - Added wireless device detection
2021-10-07 23:25:59 +02:00
f4522f7bdc
Merge pull request #241 from DarthAffe/SDK/SteelseriesKeypard5
Fixed keypad-5 issues for steelseries keyboards
2021-10-07 23:17:40 +02:00
293343a7ed Added a workaround for a SDK-issue with keypad-5 2021-10-07 23:14:21 +02:00
Robert
0edf2fc7d6 Swap the positions and make zoneOffset optional 2021-09-27 21:44:19 +02:00
Robert
4bfd890a53 Added zone offset to Logitech 2021-09-27 21:32:17 +02:00
RobertBeekman
02843705a8
Merge pull request #226 from DarthAffe/SDK/ASUS
ASUS - Keep local arrays of lights in an attempt to fix crashes
2021-09-27 19:57:18 +02:00
Diogo Trindade
4b6c341185 Moved G733 definition to PerDevice 2021-09-26 21:24:58 +01:00
afb68f1277 Refactorings to better fit general code-style; Fix for device-loading 2021-09-26 20:30:31 +02:00
Diogo Trindade
a04018fef1 Use explicit types instad of var 2021-09-23 22:04:54 +01:00
Diogo Trindade
4074b015ef Added wooting layout detection 2021-09-22 17:59:48 +01:00
Diogo Trindade
e06857e236 Removed wireless POWERPLAY definition 2021-09-22 17:56:53 +01:00
Diogo Trindade
412dc9785c Logitech - Added wireless device detection 2021-09-22 17:08:33 +01:00
Robert
21b2f774c4 Merge branch 'Development' into SDK/ASUS 2021-09-19 09:47:08 +02:00
5fc2e34f28
Merge pull request #235 from DarthAffe/SmallImprovements
Small improvements
2021-09-06 01:18:43 +02:00
cdae699c8d Improved docs 2021-09-06 01:18:16 +02:00
924b121f64 Fixed locks in RGBSurface 2021-09-06 01:15:46 +02:00
ef12e402ea Changed IReadOnlyCollections to IReadOnlyLists 2021-09-06 01:12:36 +02:00
ef3998055d Prevented unnecessary allocations 2021-09-06 01:06:49 +02:00
fab2c158af
Merge pull request #234 from DarthAffe/SmallImprovements
Small improvements
2021-09-06 00:59:03 +02:00
d487bee35c Fixed code warning 2021-09-06 00:55:48 +02:00
6974203e34 Prevented unnecessary allocations 2021-09-06 00:55:42 +02:00
Robert
ecf880d297 ASUS - Only keep local arrays of lights in an attempt to fix crashes 2021-09-03 21:57:11 +02:00
Robert Beekman
e645e4dcf2
Merge pull request #230 from diogotr7/SDK/Wooting
Updated wooting device provider to support newer keyboards
2021-09-03 21:54:59 +02:00
Robert Beekman
3fbc4b997e
Merge pull request #232 from DarthAffe/SDK/CoolerMaster
CoolerMaster - Added MM830 LED definitions
2021-09-03 20:40:35 +02:00
Robert Beekman
3074a2b7bf
Merge pull request #231 from DarthAffe/Core/UpdateTriggerCancel
Fix race condition in TimerUpdateTrigger stop
2021-09-03 20:40:14 +02:00
Robert
e73fb45503 Merge remote-tracking branch 'origin/SDK/ASUS' into SDK/ASUS 2021-09-01 20:18:53 +02:00
Robert
06f46811f4 ASUS - Keep local arrays of keys and lights in an attempt to fix crashes 2021-09-01 20:18:44 +02:00
Robert
6f4c07d65f CoolerMaster - Added MM830 LED definitions 2021-09-01 20:16:29 +02:00
Robert
1fa466809e Fix race condition in TimerUpdateTrigger stop 2021-08-23 18:48:19 +02:00
Diogo Trindade
17bd13b995 Updated wooting device provider to support newer keyboards 2021-08-22 01:52:25 +01:00
e332d79a47
Merge pull request #228 from DarthAffe/CodeIssues
Added additional LedIds for bigger setups
2021-08-07 20:06:25 +02:00
1bae7fd3de
Merge pull request #227 from DarthAffe/CodeIssues
Preparing for first major release
2021-08-07 17:52:20 +02:00
b934e97ab2 Added additional LedIds for bigger setups 2021-08-03 20:54:14 +02:00
e279ea583e Replaced deprecated features and updated nuget packages 2021-08-01 17:22:51 +02:00
4dbeaf802a Fixed code issues 2021-08-01 16:53:21 +02:00
7c0a216ccd Fixed code issues 2021-08-01 16:40:13 +02:00
59e11e5b82 Fixed code issues 2021-07-31 16:59:51 +02:00
Robert
7520147ff8 ASUS - Keep local arrays of keys and lights in an attempt to fix crashes 2021-07-26 09:12:29 +02:00
53cc40002c
Merge pull request #225 from DarthAffe/SDK/CorsairWorkaround
Sdk/corsair workaround
2021-07-03 00:08:10 +02:00
eedeadabda Added workaround for corsair custom devices with wrongly reported channel counts 2021-07-02 22:23:35 +02:00
95eeef2d04 Added corsair device id 2021-07-02 22:17:12 +02:00
790f55224d
Merge pull request #221 from piechade/Logitech
G733 Headset fix
2021-06-03 20:26:58 +02:00
85ee01f07b Fixed mapping issue in the logitech-provider 2021-06-03 20:25:40 +02:00
7b0e9152fd Fixed code issues 2021-06-01 23:53:56 +02:00
4466acfbf7 Fixed compiler messages 2021-06-01 22:19:42 +02:00
Dennis Piecha
b2909a8299 G733 Headset fix 2021-06-01 21:37:09 +02:00
c3a6182dfe
Merge pull request #220 from DarthAffe/SDK/Corsair
Updated Corsair-provider withe the SDK changes of version 3.0.378
2021-06-01 21:15:42 +02:00
c372f93215 Updated Corsair-provider withe the SDK changes of version 3.0.378 2021-06-01 21:08:04 +02:00
28c4a76b65
Merge pull request #218 from OmegaRogue/Development
Add Razer Naga 2020 Left Handed Edition
2021-05-23 15:35:47 +02:00
OmegaRogue
e3bd54df0f
Add Razer Naga 2020 Left Handed Edition
Add Support for Razer Naga 2020 Left Handed Edition to Razer Device Plugin

Signed-off-by: OmegaRogue <omegarogue@omegavoid.codes>
2021-05-23 15:03:52 +02:00
Robert Beekman
df146ceaa4
Merge pull request #217 from DarthAffe/SDK/Razer
Razer - Fixed programmable keys IDs
2021-05-23 11:36:49 +02:00
Robert
f948280596 Razer - Fixed programmable keys IDs 2021-05-22 23:07:13 +02:00
8bfc2443ac
Merge pull request #214 from DarthAffe/Core/IdGeneration
Improved DeviceName-generation to be more consistent after provider disposes
2021-05-16 13:49:16 +02:00
fb6a94f8b2 Improved DeviceName-generation to be more consistent after provider disposes.
This addresses #198
2021-05-15 23:14:07 +02:00
Robert Beekman
d3335b20ae
Merge pull request #212 from DarthAffe/SDK/Logitech
Logitech PID corrections
2021-05-13 19:45:47 +02:00
Robert Beekman
faf7535eaf
Merge pull request #213 from DarthAffe/SDK/Wooting
Wooting - Fixed race condition during SDK load/unload
2021-05-13 19:45:33 +02:00
Robert Beekman
17db44d007
Merge pull request #192 from DarthAffe/SDK/ASUS
ASUS updates & tweaks
2021-05-13 19:45:18 +02:00
91d380ed40
Merge pull request #211 from DarthAffe/SDK/Corsair
Sdk/corsair
2021-05-06 21:24:09 +02:00
Robert
41f9d82edc Wooting - Fixed race condition during SDK load/unload 2021-05-06 21:18:34 +02:00
Robert
20809f2835 Logitech - Removed duplicate G213 HID definition
Logitech - Removed incorrect G512 PID
Logitech - Corrected G513 Carbon PID
Logitech - Added G610 PID
2021-05-06 14:46:06 +02:00
cbe2368c84 Added missing mappings for recent corsair keyboards
This fixes #210
2021-04-29 20:42:08 +02:00
17f7681d33 Added LedIds for keyboard programming- an profile-buttons 2021-04-29 20:29:03 +02:00
Robert
4055ee9b0a ASUS - Refactored to use LedMappings
ASUS - Added laptop model detection
ASUS - Added the option to provide extra LED mappings to keyboards based on model
ASUS - Removed old attempts at detecting non-key LEDs on keyboards
2021-04-29 17:20:24 +02:00
Robert
6254eae845 Merge branch 'Development' into SDK/ASUS 2021-04-29 15:46:05 +02:00
Robert Beekman
63498eb8c3
Merge pull request #202 from diogotr7/razer-mapping
Razer - Added keyboard and mouse ledid mapping
2021-04-29 12:09:46 +02:00
9241673e7f Small fixes for new razer-mappings 2021-04-27 20:39:29 +02:00
f616390b84
Merge pull request #209 from DarthAffe/Typo
Fixed typo
2021-04-27 18:06:50 +02:00
363fc4c066 Fixed typo 2021-04-27 18:06:21 +02:00
a2a7758978
Merge pull request #206 from DarthAffe/SDK/PicoPi
Sdk/pico pi
2021-04-27 17:54:03 +02:00
Robert
6665e14e1d ASUS - Explain what is going on with LED tagging 2021-04-26 22:58:52 +02:00
5fbf716a87
Merge pull request #208 from DarthAffe/ExceptionHandling
Exception handling
2021-04-26 22:33:23 +02:00
b300ac8451 Improved Exception-handling in device providers 2021-04-26 22:09:54 +02:00
68c5990ccd Imporoved exception-handling in device providers 2021-04-26 21:56:30 +02:00
16aa017e77 Fixed wrong id fetch and added config methods to PpicoSDK 2021-04-25 02:26:52 +02:00
5e7ef211f7 Small code cleanup 2021-04-24 20:46:52 +02:00
7b1d8cfcd9
Rename index.md to ReadMe.md 2021-04-24 20:42:58 +02:00
5d3071235b Added PicoPi device provider 2021-04-24 20:41:55 +02:00
a1955ec377 Added LastUpdateTime to update-triggers 2021-04-24 20:39:05 +02:00
41edda99f0 Added public UpdateTriggers list to DeviceProviders 2021-04-24 20:38:30 +02:00
1d57f4e988 Fixed naming of deviceInfo parameter in AbsvtractRGBDevice 2021-04-24 20:37:51 +02:00
Robert
d6054cf527 ASUS - Added mechanism to add extra LEDs via lights 2021-04-23 22:11:00 +02:00
Robert
e86003f1ea ASUS - Attempt to add lights that aren't present as keys 2021-04-22 17:30:19 +02:00
Robert
ca264a4603 Merge branch 'Development' into SDK/ASUS 2021-04-22 16:36:24 +02:00
b39188474f
Merge pull request #205 from DarthAffe/SteelSeries
Fixed Steelseries devices not refreshing correctly and timing out
2021-04-19 22:10:31 +02:00
e3451fe480 Fixed Steelseries devices not refreshing correctly and timing out 2021-04-19 22:05:23 +02:00
b8ccc16f49
Merge pull request #203 from DarthAffe/SDK/Corsair
Sdk/corsair
2021-04-19 20:31:34 +02:00
8366adbbb0 Fixed wrong loop condition in corsair custom devices 2021-04-19 19:43:28 +02:00
1e229324be Rewrote Corsair device handling to use mappings 2021-04-18 19:33:12 +02:00
Diogo Trindade
da4c227684 Added mouse led map 2021-04-17 21:53:46 +01:00
Diogo Trindade
6bbcf987b3 Razer - added keyboard led mapping 2021-04-17 21:31:53 +01:00
2d0fd93b5f
Merge pull request #201 from DarthAffe/DeviceHids
Device hids
2021-04-17 20:35:14 +02:00
bdad1982cd Added logitech G900 PID
This fixes #133
2021-04-17 20:34:32 +02:00
9b66cf0260 Added logitech G513 Carbon PID
This fixes #168
2021-04-17 20:31:30 +02:00
d9c5a4b258 Added SteelSeries Rival 650 PID
This fixes #177
2021-04-17 20:27:53 +02:00
25198dabb9
Merge pull request #195 from DarthAffe/Core/HID
Added Led-Mapping base class and centralized HID-device-detection
2021-04-17 20:17:41 +02:00
19ecd3c8be
Merge pull request #200 from DarthAffe/Devices/DMX
DMX - Fix default random CID creation
2021-04-17 01:48:42 +02:00
Robert
0ac1858627 DMX - Fix default random CID creation 2021-04-16 17:41:28 +02:00
Robert
9343d6ee29 Logitech - Use LogiLedSetLightingForKeyWithKeyName for per key devices 2021-04-14 23:27:49 +02:00
07a4b0081f
Merge pull request #196 from DarthAffe/SDK/Asus_Workaround
Added (for testing) workaround for asus devices
2021-04-05 16:04:28 +02:00
313661a532 Added (for testing) workaround for asus devices 2021-04-05 14:54:00 +02:00
f8d007b7cd Added check to prevent updates of unmapped leds on logitech devices 2021-04-02 21:48:10 +02:00
7f3abaefec Added Led-Mapping base class and centralized HID-device-detection 2021-04-02 00:32:22 +02:00
Robert Beekman
7f0ca72c7b
Merge pull request #194 from DarthAffe/Layouts
Small layout fixes
2021-04-02 00:04:50 +02:00
Robert
922488358a Layouts - Removed public interface property 2021-04-01 23:28:42 +02:00
Robert
6545c25c2d Layouts - Added missing Author property
Layouts - Removed  Lighting element from xsd
2021-04-01 10:46:19 +02:00
Robert
9bfc53087c ASUS - Cleaned up code and removed strangly placed per-device logic 2021-03-29 16:17:14 +02:00
Robert
40cce7aaaf Core - Added Unknown LED IDs
Core - Moved Custom LED IDs from 0xFF to 0xFE
ASUS - Use Unknown LED IDs for missing LED mappings
2021-03-29 00:59:26 +02:00
Robert
c446673c3c Merge branch 'Development' into SDK/ASUS 2021-03-28 16:13:49 +02:00
Robert
a52bb474ef ASUS - Keyboards guard against double LEDs
ASUS - Keyboards return the correct AsusLed in GetLedCustomData
2021-03-27 14:39:51 +01:00
eb3b91d8d8
Merge pull request #191 from DarthAffe/UniqueDeviceNames
Added Helper to create unique device names for all devices
2021-03-26 23:38:30 +01:00
Robert
c963bcd9d7 ASUS - Added more undocumented LED IDs
ASUS - Reversed LED mapping to have the ASUS ID as the key
ASUS - Throw informative exception on missing LED mapping
2021-03-26 23:15:40 +01:00
9907dd21ec Added Helper to create unique device names for all devices 2021-03-26 21:49:10 +01:00
Robert Beekman
82ca48d933
Merge pull request #187 from DarthAffe/SDK/Razer
Razer improvements & missing PIDs
2021-03-26 21:22:10 +01:00
36365e9de8
Merge pull request #190 from DarthAffe/Core/TextureImprovement
Added int overload for PixelTexture
2021-03-25 21:51:35 +01:00
bf0f6160e3 Added int overload for PixelTexture 2021-03-25 21:50:32 +01:00
Robert Beekman
e8fc81ca6c
Merge pull request #188 from DarthAffe/SDK/Logitech
Logitech - Added G733 PID
2021-03-24 15:02:19 +01:00
Robert
5d83c698d4 Logitech - Added G733 PID 2021-03-20 22:01:53 +01:00
Robert
f03ae301aa Razer - Added endpoint types to device info
Razer - Only add the first device of each endpoint type
Razer - Fixed mouse effect ID
2021-03-17 22:27:42 +01:00
Robert Beekman
ebc62f5e5c
Merge pull request #185 from DarthAffe/SDK/CoolerMaster
CoolerMaster - Update SDK & added missing devices
2021-03-11 21:54:58 +01:00
aaddb63c63
Merge pull request #184 from Cheerpipe/G815_PID
G815 PID
2021-03-11 20:51:22 +01:00
Robert
595bc702b7 Cooler Master - Color matrix fixes 2021-03-10 22:09:02 +01:00
Cheerpipe
e8d8cf9380 G815 PID 2021-03-09 21:45:17 -03:00
Robert
8ff4c0b243 CoolerMaster - Added missing SDK-supported keyboards
CoolerMaster - Added mappings for every SDK-supported keyboard
CoolerMaster - Fixed enabling/disabling the provider at runtime
Core - Added Keyboard.Function LED ID
2021-03-06 22:55:10 +01:00
d14f272ba9
Merge pull request #183 from DarthAffe/Core/Rendering
Rewrites/improvements in rendering and device handling
2021-03-06 00:09:28 +01:00
e6290ae480 Fixed leds not updating 2021-03-05 22:59:09 +01:00
438e5c576a Small refactoring 2021-03-05 21:45:56 +01:00
f14e3c801d Moved all methods not directly tied to the surface in an extension and refactored led groups 2021-03-05 21:43:12 +01:00
47fd3ff203 Fixed devices not beeing updated 2021-03-05 21:40:51 +01:00
20347cf221 Added overridable CreateUpdateTrigger to AbstractRGBDeviceProvider 2021-03-05 10:23:17 +01:00
28d8335ee0 Merge 2021-03-04 23:56:42 +01:00
2a9a43683c Streamlined device loading/handling in device providers 2021-03-04 23:33:00 +01:00
Robert Beekman
86a040ccbf
Merge pull request #181 from DarthAffe/SDK/SteelSeries
SteelSeries devices & Monitor device type
2021-03-04 19:05:07 +01:00
Robert
f99973c386 SteelSeries - Added QCK Prism PID & zone definition
SteelSeries - Added MGP27C & zone definition
Core - Added Monitor device type
2021-03-04 11:54:18 +01:00
f6f3e9185c Moved color-correction logic deeper down to the device 2021-03-03 23:29:31 +01:00
2222808c23 Changed update-Pprameter away from dictionary 2021-03-03 23:06:22 +01:00
9c8d67740d Added custom stride parameter to PixelTexture 2021-03-01 22:49:31 +01:00
Robert Beekman
7e4c957f38
Merge pull request #178 from DarthAffe/SDK/Logitech
Logitech - Fixed LED mappings to ISO/ANSI compatiblity
2021-02-28 09:55:18 +01:00
Robert Beekman
791001af26
Merge pull request #179 from DarthAffe/SDK/Razer
Razer - Moved to HID-based device detection
2021-02-28 09:55:07 +01:00
Robert
587ae8f6e2 Razer - Moved to HID-based device detection
Razer - Added all OpenRazer provided PIDs
Razer - Use device-specific SDK endpoints, allowing access to all Razer devices
2021-02-27 12:00:12 +01:00
0780f37852 Added check for empty or one pixel requrests in Texture 2021-02-27 00:26:20 +01:00
Robert
16dc4be065 Logitech - Fixed LED mappings to ISO/ANSI compatiblity 2021-02-26 14:34:56 +01:00
fe2f6e00f1 Fixed some erorrs in color calculations 2021-02-26 01:03:54 +01:00
1975b9bf48 Added XYZ, Lab and LCh color-methods 2021-02-26 00:31:16 +01:00
105fdea2d7 Small optimizations 2021-02-25 00:30:09 +01:00
cc7abb63f0 Simplified textures 2021-02-24 13:05:15 +01:00
8ddf0bc734 Moved LedGroupExtension to Core 2021-02-24 13:05:06 +01:00
6d7b7ad2e2 Merge branch 'Development' into Core/Rendering 2021-02-23 23:01:53 +01:00
b857fb2c81 Changed Data-Spans in texture to be readonly 2021-02-23 23:01:04 +01:00
c9634f3913 Optimized Textures and Sampler 2021-02-23 01:38:53 +01:00
34a2acc6c4 Added missing in-parameters 2021-02-22 21:53:48 +01:00
dade203c94 Changed Brush Render-Method to be virtual; Added list not empty check to Decorator application 2021-02-22 21:09:51 +01:00
520fb38602 Added generic base-class for PixelTexture 2021-02-22 00:46:48 +01:00
b328032df0 Changed everything to use floats instead of doubles 2021-02-22 00:46:18 +01:00
13afc29987 Reworked rendering 2021-02-21 15:49:05 +01:00
0e30c93db4
Merge pull request #174 from DarthAffe/Core/DeviceHandling
Added physical-layout to KeyboardDeviceInfo
2021-02-20 23:11:43 +01:00
f00ab7f581 Added abnt and ks keyboard-layouts 2021-02-13 20:09:04 +01:00
190f5df025 Added physical-layout to KeyboardDeviceInfo 2021-02-13 18:28:24 +01:00
Robert
d5c9ec2e26 Debug device provider - Create LEDs when loading layout 2021-02-13 17:22:26 +01:00
4edc665403
Merge pull request #173 from DarthAffe/Core/DeviceHandling
Renamed SurfaceRectangle and fixed spelling mistake
2021-02-09 23:18:06 +01:00
07a6d8952c Renamed SurfaceRectangle and fixed spelling mistake 2021-02-09 23:17:11 +01:00
37de5d95f4
Merge pull request #172 from DarthAffe/Core/DeviceHandling
Added base class for location/size-handling and refactored devices an…
2021-02-09 23:11:50 +01:00
d44223ee6a Added base class for location/size-handling and refactored devices and leds to make use of it 2021-02-09 23:11:01 +01:00
cc01fa74d6
Merge pull request #170 from DarthAffe/Core/DeviceHandling
Core/device handling
2021-02-06 19:58:41 +01:00
6a4ebb3d2a Refactoring 2021-02-06 17:07:41 +01:00
a0a1521658 Fixed warnings 2021-02-02 23:37:56 +01:00
57dc9afb32 Updated device providers to make everything compile again 2021-02-01 23:13:15 +01:00
6619539176 Reworked Layouts to be more versatile 2021-01-31 14:35:06 +01:00
4a170e6af7 Extracted layout-logic to own project 2021-01-02 17:17:17 +01:00
226d29156d Removed layouts from devices 2021-01-02 17:01:44 +01:00
d4bb0bd9fd Updated core to correctly use nullable reference types; first changes for new device handling 2020-12-31 20:07:51 +01:00
5bc945c4f7 Removed static RGBSurface and DeviceSpecialParts 2020-12-29 15:34:35 +01:00
53c4b8188d Merged surface-partials into single file 2020-12-29 15:13:39 +01:00
81ad871f94
Merge pull request #167 from DarthAffe/SDK/Razer
Change default binary search paths for razer
2020-12-25 23:21:39 +01:00
2e79d672b5 Change default binary search paths for razer 2020-12-25 23:20:09 +01:00
d69c3a96dd
Merge pull request #165 from DarthAffe/Core/SyncBackRemoval
Removed SyncBack
2020-12-25 22:24:28 +01:00
04f07d76a2 Removed SyncBack 2020-12-25 19:45:08 +01:00
5243cdc6ee
Merge pull request #162 from DarthAffe/SDK/Corsair
Added missing Corsair channel device types
2020-12-25 19:33:00 +01:00
e418710f3e
Merge pull request #161 from DarthAffe/SDK/BitwizardFix
Added pin-selectionn for Bitwizard devices
2020-12-25 19:32:50 +01:00
412cbd8c98
Merge pull request #164 from DarthAffe/Net5
Net5
2020-12-25 19:32:41 +01:00
b37584018a
Merge pull request #160 from diogotr7/SDK/Razer
Updated razer device provider with new Keyboard and mousepad vs grids
2020-12-25 19:32:17 +01:00
8f37292839
Merge pull request #163 from DarthAffe/Net5
Net5
2020-12-25 19:32:07 +01:00
3e6068b090 Added missing Corsair channel device types 2020-12-24 23:30:53 +01:00
87655aba5d Added pin-selection nfor Bitwizard devices 2020-12-24 22:52:52 +01:00
ceeaf35d27
Merge pull request #158 from Fma965/master
Added Logitech G915 TKL HID and G502 Wireless HID and correct some incorrect information
2020-12-24 00:54:53 +01:00
1e544b0cbd
Merge pull request #159 from RafeeSamith/master
Added even more Steelseries PIDs
2020-12-24 00:54:01 +01:00
Diogo Trindade
04c4c1ea1e Updated razer device provider with new Keyboard and mousepad vs grids 2020-12-21 00:46:01 +00:00
a4ed4c466b Updated Asus-device-detection 2020-12-17 17:44:34 +01:00
074fcc8203 Removed legacy Asus-Project 2020-12-17 16:29:01 +01:00
Rafee
53a10c30b9
Added even more PIDs
Added Devices:
- Aerox 3 (Wired and Wireless)
- Rival 100/105/106/110/150
- Rival 300
- Rival 700
- Sensei Ten

- Apex M800
- Apex Pro
- Apex Pro TKL
2020-12-10 23:23:11 +03:00
0907a78efc enabled nullable reference types 2020-12-05 18:13:39 +01:00
Fma965
d6ecc9981f Added G502 Lightsped / Wireless HID 2020-12-05 15:52:25 +00:00
Fma965
c1a81f2ec7 Corrected some names 2020-12-05 15:32:14 +00:00
Fma965
8fc4099c17 Added Logitech G915 TKL HID 2020-12-05 14:22:15 +00:00
66d896edd6 Updated nuget-packages 2020-11-12 17:13:27 +01:00
42456ad7bd Changed Json-Serializer to .NET Default 2020-11-12 17:13:19 +01:00
fdb5022197 Changed all projects to target .NET 5 2020-11-10 23:35:02 +01:00
650 changed files with 46416 additions and 29263 deletions

56
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,56 @@
name: RGB.NET-CI
on:
workflow_dispatch:
inputs:
version:
description: 'version'
required: true
type: string
increment:
required: true
type: string
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
9.0.x
8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore --configuration Release /p:Version=${{ github.event.inputs.version }}-prerelease.${{ github.event.inputs.increment }}
- name: Test
run: dotnet test --no-build --verbosity normal --configuration Release
- name: Upload a Build Artifact NET9
uses: actions/upload-artifact@v4.3.1
with:
name: RGB.NET-NET9
path: bin/net9.0/RGB.NET.*.dll
if-no-files-found: error
- name: Upload a Build Artifact NET8
uses: actions/upload-artifact@v4.3.1
with:
name: RGB.NET-NET8
path: bin/net8.0/RGB.NET.*.dll
if-no-files-found: error
- name: Upload Nuget Build Artifact
uses: actions/upload-artifact@v4.3.1
with:
name: RGB.NET-Nugets
path: bin/*nupkg
if-no-files-found: error
- name: Nuget Push
run: dotnet nuget push **\*.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json
- name: Symbols Push
run: dotnet nuget push **\*.snupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json

26
.github/workflows/pr_verify.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: PR-Verify
on:
pull_request:
branches: [ master, Development ]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4.1.1
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
9.0.x
8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore --configuration Release /p:Version=0.0.0
- name: Test
run: dotnet test --no-build --verbosity normal --configuration Release

56
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,56 @@
name: RGB.NET-Release
on:
workflow_dispatch:
inputs:
version:
description: 'version'
required: true
type: string
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4.1.1
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
9.0.x
8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore --configuration Release /p:Version=${{ github.event.inputs.version }}
- name: Test
run: dotnet test --no-build --verbosity normal --configuration Release
- name: Upload a Build Artifact NET9
uses: actions/upload-artifact@v4.3.1
with:
name: RGB.NET-NET9
path: bin/net9.0/RGB.NET.*.dll
if-no-files-found: error
- name: Upload a Build Artifact NET8
uses: actions/upload-artifact@v4.3.1
with:
name: RGB.NET-NET8
path: bin/net8.0/RGB.NET.*.dll
if-no-files-found: error
- name: Upload Nuget Build Artifact
uses: actions/upload-artifact@v4.3.1
with:
name: RGB.NET-Nugets
path: bin/*nupkg
if-no-files-found: error
- name: Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ github.event.inputs.version }}
generate_release_notes: true
files: bin/net9.0/RGB.NET.*.dll
- name: Nuget Push
run: dotnet nuget push **\*.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json

View File

@ -7,17 +7,22 @@
<xsd:element name="Description" type="xsd:string" />
<xsd:element name="Author" type="xsd:string" />
<xsd:element name="Type" type="xsd:string" />
<xsd:element name="Lighting" type="xsd:string" />
<xsd:element name="Vendor" type="xsd:string" />
<xsd:element name="Model" type="xsd:string" />
<xsd:element name="Shape" type="xsd:string" />
<xsd:element name="Width" type="xsd:double" />
<xsd:element name="Height" type="xsd:double" />
<xsd:element name="ImageBasePath" type="xsd:string" />
<xsd:element name="DeviceImage" type="xsd:string" />
<xsd:element name="LedUnitWidth" type="xsd:double" />
<xsd:element name="LedUnitHeight" type="xsd:double" />
<xsd:element name="Leds">
<xsd:element name="CustomData">
<xsd:complexType>
<xsd:sequence>
<xsd:any />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Leds">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="unbounded" name="Led">
@ -28,6 +33,13 @@
<xsd:element name="Y" type="xsd:string" />
<xsd:element name="Width" type="xsd:string" />
<xsd:element name="Height" type="xsd:string" />
<xsd:element name="CustomData">
<xsd:complexType>
<xsd:sequence>
<xsd:any />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="Id" type="xsd:string" use="required" />
</xsd:complexType>
@ -35,32 +47,7 @@
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="LedImageLayouts">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="unbounded" name="LedImageLayout">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="LedImages">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="unbounded" name="LedImage">
<xsd:complexType>
<xsd:attribute name="Id" type="xsd:string" use="required" />
<xsd:attribute name="Image" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="Layout" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xs:schema>

View File

@ -1,30 +1,87 @@
# RGB.NET
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/DarthAffe/RGB.NET?style=for-the-badge)](https://github.com/DarthAffe/RGB.NET/releases)
[![Nuget](https://img.shields.io/nuget/v/RGB.NET.Core?style=for-the-badge)](https://www.nuget.org/packages?q=rgb.net)
[![GitHub](https://img.shields.io/github/license/DarthAffe/RGB.NET?style=for-the-badge)](https://github.com/DarthAffe/RGB.NET/blob/master/LICENSE)
[![GitHub Repo stars](https://img.shields.io/github/stars/DarthAffe/RGB.NET?style=for-the-badge)](https://github.com/DarthAffe/RGB.NET/stargazers)
[![Discord](https://img.shields.io/discord/366163308941934592?logo=discord&logoColor=white&style=for-the-badge)](https://discord.gg/9kytURv)
This project aims to unify the use of various RGB-devices.
**It is currently under heavy development and will have breaking changes in the future!** Right now a lot of devices aren't working as expected and there are bugs/unfinished features. Please think about that when you consider using the library in this early stage.
If you want to help with layouting/testing devices or if you need support using the library feel free to join the [RGB.NET discord-channel](https://discord.gg/9kytURv).
> **IMPORTANT NOTE**
This is a library to integrate RGB-devices into your own application. It does not contain any executables!
If you're looking for a full blown software solution to manage your RGB-devices, take a look at [Artemis](https://artemis-rgb.com/).
## Getting Started
### Setup
1. Add the [RGB.NET.Core](https://www.nuget.org/packages/RGB.NET.Core) and [Devices](https://www.nuget.org/packages?q=rgb.net.Devices)-Nugets for all devices you want to use.
2. For some of the vendors SDK-libraries are needed. Check the contained Readmes for more information in that case.
3. Create a new `RGBSurface`.
```csharp
RGBSurface surface = new RGBSurface();
```
## Adding prerelease packages using NuGet ##
This is the easiest and therefore preferred way to include RGB.NET in your project.
4. Initialize the providers for all devices you want to use and add the devices to the surface. For example:
```csharp
CorsairDeviceProvider.Instance.Initialize(throwExceptions: true);
surface.Attach(CorsairDeviceProvider.Instance.Devices);
```
The `Initialize`-method allows to load only devices of specific types by setting a filter and for debugging purposes allows to enable exception throwing. (By default they are catched and provided through the `Exception`-event.)
You can also use the `Load`-Extension on the surface.
```csharp
surface.Load(CorsairDeviceProvider.Instance);
```
> While most device-providers are implemented in a way that supports fast loading like this some may have a different loading procedures. (For example the `WS281XDeviceProvider` requires device-definitions before loading.)
Since there aren't any release-packages right now you'll have to use the CI-feed from [http://nuget.arge.be](http://nuget.arge.be).
You can include it either by adding ```http://nuget.arge.be/v3/index.json``` to your Visual Studio package sources or by adding this [NuGet.Config](https://github.com/DarthAffe/RGB.NET/tree/master/Documentation/NuGet.Config) to your project (at the same level as your solution).
5. Add an update-trigger. In most cases the TimerUpdateTrigger is preferable, but you can also implement your own to fit your needs.
```csharp
surface.RegisterUpdateTrigger(new TimerUpdateTrigger());
```
> If you want to trigger updates manually the `ManualUpdateTrigger` should be used.
### .NET 4.5 Support ###
At the end of the year with the release of .NET 5 the support for old .NET-Framwork versions will be droppped!
It's not recommended to use RGB.NET in projects targeting .NET 4.x that aren't planned to be moved to Core/.NET 5 in the future.
6. *This step is optional but recommended.* For rendering the location of each LED on the surface can be important. Since not all SDKs provide useful layout-information you might want to add Layouts to your devices. (TODO: add wiki article for this)
Same goes for the location of the device on the surface. If you don't care about the exact location of the devices you can use:
```csharp
surface.AlignDevices();
```
The basic setup is now complete and you can start setting up your rendering.
### Device-Layouts
To be able to have devices with correct LED-locations and sizes they need to be layouted. Pre-created layouts can be found at https://github.com/DarthAffe/RGB.NET-Resources.
### Basic Rendering
As an example we'll add a moving rainbow over all devices on the surface.
1. Create a led-group containing all leds on the surface (all devices)
```csharp
ILedGroup allLeds = new ListLedGroup(surface, surface.Leds);
```
If you plan to create layouts for your own devices check out https://github.com/DarthAffe/RGB.NET/wiki/Creating-Layouts first. There's also a layout-editor which strongly simplifies most of the work: https://github.com/SpoinkyNL/RGB.NET-Layout-Editor
2. Create a rainbow-gradient.
```csharp
RainbowGradient rainbow = new RainbowGradient();
```
### Example usage of RGB.NET
[![Example video](https://img.youtube.com/vi/JLRa0Wv4qso/0.jpg)](http://www.youtube.com/watch?v=JLRa0Wv4qso)
3. Add a decorator to the gradient to make it move. (Decorators are
```csharp
rainbow.AddDecorator(new MoveGradientDecorator(surface));
```
#### Example Projects
[https://github.com/DarthAffe/KeyboardAudioVisualizer](https://github.com/DarthAffe/KeyboardAudioVisualizer)
[https://github.com/DarthAffe/RGBSyncPlus](https://github.com/DarthAffe/RGBSyncPlus)
4. Create a texture (the size - in this example 10, 10 - is not important here since the gradient shoukd be stretched anyway)
```csharp
ITexture texture = new ConicalGradientTexture(new Size(10, 10), rainbow);
```
5. Add a brush rendering the texture to the led-group
```csharp
allLeds.Brush = new TextureBrush(texture);
```
### Full example
```csharp
RGBSurface surface = new RGBSurface();
surface.Load(CorsairDeviceProvider.Instance);
surface.AlignDevices();
surface.RegisterUpdateTrigger(new TimerUpdateTrigger());
ILedGroup allLeds = new ListLedGroup(surface, surface.Leds);
RainbowGradient rainbow = new RainbowGradient();
rainbow.AddDecorator(new MoveGradientDecorator(surface));
ITexture texture = new ConicalGradientTexture(new Size(10, 10), rainbow);
allLeds.Brush = new TextureBrush(texture);
```

View File

@ -1,119 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable ReturnTypeCanBeEnumerable.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using System;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc cref="AbstractBrush" />
/// <inheritdoc cref="IGradientBrush" />
/// <summary>
/// Represents a brush drawing a conical gradient.
/// </summary>
public class ConicalGradientBrush : AbstractBrush, IGradientBrush
{
#region Properties & Fields
private float _origin = (float)Math.Atan2(-1, 0);
/// <summary>
/// Gets or sets the origin (radian-angle) this <see cref="ConicalGradientBrush"/> is drawn to. (default: -π/2)
/// </summary>
public float Origin
{
get => _origin;
set => SetProperty(ref _origin, value);
}
private Point _center = new Point(0.5, 0.5);
/// <summary>
/// Gets or sets the center <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="ConicalGradientBrush"/>. (default: 0.5, 0.5)
/// </summary>
public Point Center
{
get => _center;
set => SetProperty(ref _center, value);
}
private IGradient _gradient;
/// <inheritdoc />
/// <summary>
/// Gets or sets the gradient drawn by the brush. If null it will default to full transparent.
/// </summary>
public IGradient Gradient
{
get => _gradient;
set => SetProperty(ref _gradient, value);
}
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
/// </summary>
public ConicalGradientBrush()
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
/// </summary>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
public ConicalGradientBrush(IGradient gradient)
{
this.Gradient = gradient;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
/// </summary>
/// <param name="center">The center <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
public ConicalGradientBrush(Point center, IGradient gradient)
{
this.Center = center;
this.Gradient = gradient;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" /> class.
/// </summary>
/// <param name="center">The center <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
/// <param name="origin">The origin (radian-angle) the <see cref="T:RGB.NET.Core.IBrush" /> is drawn to.</param>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.ConicalGradientBrush" />.</param>
public ConicalGradientBrush(Point center, float origin, IGradient gradient)
{
this.Center = center;
this.Origin = origin;
this.Gradient = gradient;
}
#endregion
#region Methods
/// <inheritdoc />
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
{
double centerX = rectangle.Size.Width * Center.X;
double centerY = rectangle.Size.Height * Center.Y;
double angle = Math.Atan2(renderTarget.Point.Y - centerY, renderTarget.Point.X - centerX) - Origin;
if (angle < 0) angle += Math.PI * 2;
double offset = angle / (Math.PI * 2);
return Gradient.GetColor(offset);
}
#endregion
}
}

View File

@ -1,17 +0,0 @@
using RGB.NET.Brushes.Gradients;
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc />
/// <summary>
/// Represents a basic gradient-brush.
/// </summary>
public interface IGradientBrush : IBrush
{
/// <summary>
/// Gets the <see cref="IGradient"/> used by this <see cref="IGradientBrush"/>.
/// </summary>
IGradient Gradient { get; }
}
}

View File

@ -1,109 +0,0 @@
// ReSharper disable CollectionNeverUpdated.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable ReturnTypeCanBeEnumerable.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using RGB.NET.Brushes.Gradients;
using RGB.NET.Brushes.Helper;
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc cref="AbstractBrush" />
/// <inheritdoc cref="IGradientBrush" />
/// <summary>
/// Represents a brush drawing a linear gradient.
/// </summary>
public class LinearGradientBrush : AbstractBrush, IGradientBrush
{
#region Properties & Fields
private Point _startPoint = new Point(0, 0.5);
/// <summary>
/// Gets or sets the start <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="LinearGradientBrush"/>. (default: 0.0, 0.5)
/// </summary>
public Point StartPoint
{
get => _startPoint;
set => SetProperty(ref _startPoint, value);
}
private Point _endPoint = new Point(1, 0.5);
/// <summary>
/// Gets or sets the end <see cref="Point"/> (as percentage in the range [0..1]) of the <see cref="IGradient"/> drawn by this <see cref="LinearGradientBrush"/>. (default: 1.0, 0.5)
/// </summary>
public Point EndPoint
{
get => _endPoint;
set => SetProperty(ref _endPoint, value);
}
private IGradient _gradient;
/// <inheritdoc />
public IGradient Gradient
{
get => _gradient;
set => SetProperty(ref _gradient, value);
}
#endregion
#region Constructor
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
/// </summary>
public LinearGradientBrush()
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
/// </summary>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.LinearGradientBrush" />.</param>
public LinearGradientBrush(IGradient gradient)
{
this.Gradient = gradient;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.LinearGradientBrush" /> class.
/// </summary>
/// <param name="startPoint">The start <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
/// <param name="endPoint">The end <see cref="T:RGB.NET.Core.Point" /> (as percentage in the range [0..1]).</param>
/// <param name="gradient">The <see cref="T:RGB.NET.Brushes.Gradients.IGradient" /> drawn by this <see cref="T:RGB.NET.Brushes.LinearGradientBrush" />.</param>
public LinearGradientBrush(Point startPoint, Point endPoint, IGradient gradient)
{
this.StartPoint = startPoint;
this.EndPoint = endPoint;
this.Gradient = gradient;
}
#endregion
#region Methods
/// <inheritdoc />
/// <summary>
/// Gets the color at an specific point assuming the brush is drawn into the given rectangle.
/// </summary>
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
/// <returns>The color at the specified point.</returns>
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
{
if (Gradient == null) return Color.Transparent;
Point startPoint = new Point(StartPoint.X * rectangle.Size.Width, StartPoint.Y * rectangle.Size.Height);
Point endPoint = new Point(EndPoint.X * rectangle.Size.Width, EndPoint.Y * rectangle.Size.Height);
double offset = GradientHelper.CalculateLinearGradientOffset(startPoint, endPoint, renderTarget.Point);
return Gradient.GetColor(offset);
}
#endregion
}
}

View File

@ -1,97 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Global
using System;
using RGB.NET.Brushes.Gradients;
using RGB.NET.Brushes.Helper;
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc cref="AbstractBrush" />
/// <inheritdoc cref="IGradientBrush" />
/// <summary>
/// Represents a brush drawing a radial gradient around a center point.
/// </summary>
public class RadialGradientBrush : AbstractBrush, IGradientBrush
{
#region Properties & Fields
private Point _center = new Point(0.5, 0.5);
/// <summary>
/// Gets or sets the center <see cref="Point"/> (as percentage in the range [0..1]) around which the <see cref="RadialGradientBrush"/> should be drawn. (default: 0.5, 0.5)
/// </summary>
public Point Center
{
get => _center;
set => SetProperty(ref _center, value);
}
private IGradient _gradient;
/// <inheritdoc />
public IGradient Gradient
{
get => _gradient;
set => SetProperty(ref _gradient, value);
}
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
/// </summary>
public RadialGradientBrush()
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
/// </summary>
/// <param name="gradient">The gradient drawn by the brush.</param>
public RadialGradientBrush(IGradient gradient)
{
this.Gradient = gradient;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.RadialGradientBrush" /> class.
/// </summary>
/// <param name="center">The center point (as percentage in the range [0..1]).</param>
/// <param name="gradient">The gradient drawn by the brush.</param>
public RadialGradientBrush(Point center, IGradient gradient)
{
this.Center = center;
this.Gradient = gradient;
}
#endregion
#region Methods
/// <inheritdoc />
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget)
{
if (Gradient == null) return Color.Transparent;
Point centerPoint = new Point(rectangle.Location.X + (rectangle.Size.Width * Center.X), rectangle.Location.Y + (rectangle.Size.Height * Center.Y));
// Calculate the distance to the farthest point from the center as reference (this has to be a corner)
// ReSharper disable once RedundantCast - never trust this ...
double refDistance = Math.Max(Math.Max(Math.Max(GradientHelper.CalculateDistance(rectangle.Location, centerPoint),
GradientHelper.CalculateDistance(new Point(rectangle.Location.X + rectangle.Size.Width, rectangle.Location.Y), centerPoint)),
GradientHelper.CalculateDistance(new Point(rectangle.Location.X, rectangle.Location.Y + rectangle.Size.Height), centerPoint)),
GradientHelper.CalculateDistance(new Point(rectangle.Location.X + rectangle.Size.Width, rectangle.Location.Y + rectangle.Size.Height), centerPoint));
double distance = GradientHelper.CalculateDistance(renderTarget.Point, centerPoint);
double offset = distance / refDistance;
return Gradient.GetColor(offset);
}
#endregion
}
}

View File

@ -1,65 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc />
/// <summary>
/// Represents a brush drawing only a single color.
/// </summary>
public class SolidColorBrush : AbstractBrush
{
#region Properties & Fields
private Color _color;
/// <summary>
/// Gets or sets the <see cref="Color"/> drawn by this <see cref="SolidColorBrush"/>.
/// </summary>
public Color Color
{
get => _color;
set => SetProperty(ref _color, value);
}
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.SolidColorBrush" /> class.
/// </summary>
/// <param name="color">The <see cref="P:RGB.NET.Brushes.SolidColorBrush.Color" /> drawn by this <see cref="T:RGB.NET.Brushes.SolidColorBrush" />.</param>
public SolidColorBrush(Color color)
{
this.Color = color;
}
#endregion
#region Methods
/// <inheritdoc />
protected override Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget) => Color;
#endregion
#region Operators
/// <summary>
/// Converts a <see cref="Color" /> to a <see cref="SolidColorBrush" />.
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert.</param>
public static explicit operator SolidColorBrush(Color color) => new SolidColorBrush(color);
/// <summary>
/// Converts a <see cref="SolidColorBrush" /> to a <see cref="Color" />.
/// </summary>
/// <param name="brush">The <see cref="Color"/> to convert.</param>
public static implicit operator Color(SolidColorBrush brush) => brush.Color;
#endregion
}
}

View File

@ -1,11 +0,0 @@
using RGB.NET.Core;
namespace RGB.NET.Brushes
{
/// <inheritdoc />
/// <summary>
/// Represents a basic decorator decorating a <see cref="T:RGB.NET.Brushes.Gradients.IGradient" />.
/// </summary>
public interface IGradientDecorator : IDecorator
{ }
}

View File

@ -1,150 +0,0 @@
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using RGB.NET.Core;
namespace RGB.NET.Brushes.Gradients
{
/// <inheritdoc cref="AbstractDecoratable{T}" />
/// <inheritdoc cref="IGradient" />
/// <summary>
/// Represents a basic gradient.
/// </summary>
public abstract class AbstractGradient : AbstractDecoratable<IGradientDecorator>, IGradient
{
#region Properties & Fields
/// <summary>
/// Gets a list of the stops used by this <see cref="AbstractGradient"/>.
/// </summary>
public ObservableCollection<GradientStop> GradientStops { get; } = new ObservableCollection<GradientStop>();
private bool _wrapGradient;
/// <summary>
/// Gets or sets if the Gradient wraps around if there isn't a second stop to take.
/// Example: There is a stop at offset 0.0, 0.5 and 0.75.
/// Without wrapping offset 1.0 will be calculated the same as 0.75; with wrapping it would be the same as 0.0.
/// </summary>
public bool WrapGradient
{
get => _wrapGradient;
set => SetProperty(ref _wrapGradient, value);
}
#endregion
#region Events
/// <inheritdoc />
public event EventHandler GradientChanged;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractGradient"/> class.
/// </summary>
protected AbstractGradient()
{
GradientStops.CollectionChanged += GradientCollectionChanged;
PropertyChanged += (sender, args) => OnGradientChanged();
}
/// <summary>
/// Initializes a new instance of the <see cref="AbstractGradient"/> class.
/// </summary>
/// <param name="gradientStops">The stops with which the gradient should be initialized.</param>
protected AbstractGradient(params GradientStop[] gradientStops)
{
GradientStops.CollectionChanged += GradientCollectionChanged;
PropertyChanged += (sender, args) => OnGradientChanged();
foreach (GradientStop gradientStop in gradientStops)
GradientStops.Add(gradientStop);
}
/// <summary>
/// Initializes a new instance of the <see cref="AbstractGradient"/> class.
/// </summary>
/// <param name="wrapGradient">Specifies whether the gradient should wrapp or not (see <see cref="WrapGradient"/> for an example of what this means).</param>
/// <param name="gradientStops">The stops with which the gradient should be initialized.</param>
protected AbstractGradient(bool wrapGradient, params GradientStop[] gradientStops)
{
this.WrapGradient = wrapGradient;
GradientStops.CollectionChanged += GradientCollectionChanged;
PropertyChanged += (sender, args) => OnGradientChanged();
foreach (GradientStop gradientStop in gradientStops)
GradientStops.Add(gradientStop);
}
#endregion
#region Methods
/// <summary>
/// Clips the offset and ensures, that it is inside the bounds of the stop list.
/// </summary>
/// <param name="offset"></param>
/// <returns></returns>
protected double ClipOffset(double offset)
{
double max = GradientStops.Max(n => n.Offset);
if (offset > max)
return max;
double min = GradientStops.Min(n => n.Offset);
return offset < min ? min : offset;
}
/// <inheritdoc />
public abstract Color GetColor(double offset);
/// <inheritdoc />
public virtual void Move(double offset)
{
offset /= 360.0;
foreach (GradientStop gradientStop in GradientStops)
gradientStop.Offset += offset;
while (GradientStops.All(x => x.Offset > 1))
foreach (GradientStop gradientStop in GradientStops)
gradientStop.Offset -= 1;
while (GradientStops.All(x => x.Offset < 0))
foreach (GradientStop gradientStop in GradientStops)
gradientStop.Offset += 1;
}
/// <summary>
/// Should be called to indicate that the gradient was changed.
/// </summary>
protected void OnGradientChanged() => GradientChanged?.Invoke(this, null);
private void GradientCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
foreach (GradientStop gradientStop in e.OldItems)
gradientStop.PropertyChanged -= GradientStopChanged;
if (e.NewItems != null)
foreach (GradientStop gradientStop in e.NewItems)
gradientStop.PropertyChanged += GradientStopChanged;
OnGradientChanged();
}
private void GradientStopChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) => OnGradientChanged();
#endregion
}
}

View File

@ -1,52 +0,0 @@
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable MemberCanBePrivate.Global
using RGB.NET.Core;
namespace RGB.NET.Brushes.Gradients
{
/// <summary>
/// Represents a stop on a gradient.
/// </summary>
public class GradientStop : AbstractBindable
{
#region Properties & Fields
private double _offset;
/// <summary>
/// Gets or sets the percentage offset to place this <see cref="GradientStop"/>. This should be inside the range of [0..1] but it's not necessary.
/// </summary>
public double Offset
{
get => _offset;
set => SetProperty(ref _offset, value);
}
private Color _color;
/// <summary>
/// Gets or sets the <see cref="Color"/> of this <see cref="GradientStop"/>.
/// </summary>
public Color Color
{
get => _color;
set => SetProperty(ref _color, value);
}
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="GradientStop"/> class.
/// </summary>
/// <param name="offset">The percentage offset to place this <see cref="GradientStop"/>.</param>
/// <param name="color">The <see cref="Color"/> of the <see cref="GradientStop"/>.</param>
public GradientStop(double offset, Color color)
{
this.Offset = offset;
this.Color = color;
}
#endregion
}
}

View File

@ -1,30 +0,0 @@
using System;
using RGB.NET.Core;
namespace RGB.NET.Brushes.Gradients
{
/// <inheritdoc />
/// <summary>
/// Represents a basic gradient.
/// </summary>
public interface IGradient : IDecoratable<IGradientDecorator>
{
/// <summary>
/// Occurs if the <see cref="IGradient"/> is changed.
/// </summary>
event EventHandler GradientChanged;
/// <summary>
/// Gets the <see cref="Color"/> of the <see cref="IGradient"/> on the specified offset.
/// </summary>
/// <param name="offset">The percentage offset to take the <see cref="Color"/> from.</param>
/// <returns>The <see cref="Color"/> at the specific offset.</returns>
Color GetColor(double offset);
/// <summary>
/// Moves the <see cref="IGradient"/> by the provided offset.
/// </summary>
/// <param name="offset">The offset the <see cref="IGradient"/> should be moved.</param>
void Move(double offset);
}
}

View File

@ -1,152 +0,0 @@
// ReSharper disable UnusedMember.Global
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using RGB.NET.Core;
namespace RGB.NET.Brushes.Gradients
{
/// <inheritdoc />
/// <summary>
/// Represents a linear interpolated gradient with n stops.
/// </summary>
public class LinearGradient : AbstractGradient
{
#region Properties & Fields
private bool _isOrderedGradientListDirty = true;
private LinkedList<GradientStop> _orderedGradientStops;
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.Gradients.LinearGradient" /> class.
/// </summary>
public LinearGradient()
{
Initialize();
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.Gradients.LinearGradient" /> class.
/// </summary>
/// <param name="gradientStops">The stops with which the gradient should be initialized.</param>
public LinearGradient(params GradientStop[] gradientStops)
: base(gradientStops)
{
Initialize();
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Brushes.Gradients.AbstractGradient" /> class.
/// </summary>
/// <param name="wrapGradient">Specifies whether the gradient should wrapp or not (see <see cref="P:RGB.NET.Brushes.Gradients.AbstractGradient.WrapGradient" /> for an example of what this means).</param>
/// <param name="gradientStops">The stops with which the gradient should be initialized.</param>
public LinearGradient(bool wrapGradient, params GradientStop[] gradientStops)
: base(wrapGradient, gradientStops)
{
Initialize();
}
#endregion
#region Methods
private void Initialize()
{
void OnGradientStopOnPropertyChanged(object sender, PropertyChangedEventArgs args) => _isOrderedGradientListDirty = true;
foreach (GradientStop gradientStop in GradientStops)
gradientStop.PropertyChanged += OnGradientStopOnPropertyChanged;
GradientStops.CollectionChanged += (sender, args) =>
{
if (args.OldItems != null)
foreach (GradientStop gradientStop in args.OldItems)
gradientStop.PropertyChanged -= OnGradientStopOnPropertyChanged;
if (args.NewItems != null)
foreach (GradientStop gradientStop in args.NewItems)
gradientStop.PropertyChanged += OnGradientStopOnPropertyChanged;
};
}
/// <inheritdoc />
/// <summary>
/// Gets the linear interpolated <see cref="T:RGB.NET.Core.Color" /> at the given offset.
/// </summary>
/// <param name="offset">The percentage offset to take the color from.</param>
/// <returns>The <see cref="T:RGB.NET.Core.Color" /> at the specific offset.</returns>
public override Color GetColor(double offset)
{
if (GradientStops.Count == 0) return Color.Transparent;
if (GradientStops.Count == 1) return GradientStops[0].Color;
if (_isOrderedGradientListDirty)
_orderedGradientStops = new LinkedList<GradientStop>(GradientStops.OrderBy(x => x.Offset));
(GradientStop gsBefore, GradientStop gsAfter) = GetEnclosingGradientStops(offset, _orderedGradientStops, WrapGradient);
double blendFactor = 0;
if (!gsBefore.Offset.Equals(gsAfter.Offset))
blendFactor = ((offset - gsBefore.Offset) / (gsAfter.Offset - gsBefore.Offset));
double colA = ((gsAfter.Color.A - gsBefore.Color.A) * blendFactor) + gsBefore.Color.A;
double colR = ((gsAfter.Color.R - gsBefore.Color.R) * blendFactor) + gsBefore.Color.R;
double colG = ((gsAfter.Color.G - gsBefore.Color.G) * blendFactor) + gsBefore.Color.G;
double colB = ((gsAfter.Color.B - gsBefore.Color.B) * blendFactor) + gsBefore.Color.B;
return new Color(colA, colR, colG, colB);
}
/// <summary>
/// Get the two <see cref="GradientStop"/>s encapsulating the given offset.
/// </summary>
/// <param name="offset">The reference offset.</param>
/// <param name="orderedStops">The ordered list of <see cref="GradientStop"/> to choose from.</param>
/// <param name="wrap">Bool indicating if the gradient should be wrapped or not.</param>
/// <returns></returns>
protected virtual (GradientStop gsBefore, GradientStop gsAfter) GetEnclosingGradientStops(double offset, LinkedList<GradientStop> orderedStops, bool wrap)
{
LinkedList<GradientStop> gradientStops = new LinkedList<GradientStop>(orderedStops);
if (wrap)
{
GradientStop gsBefore, gsAfter;
do
{
gsBefore = gradientStops.LastOrDefault(n => n.Offset <= offset);
if (gsBefore == null)
{
GradientStop lastStop = gradientStops.Last.Value;
gradientStops.AddFirst(new GradientStop(lastStop.Offset - 1, lastStop.Color));
gradientStops.RemoveLast();
}
gsAfter = gradientStops.FirstOrDefault(n => n.Offset >= offset);
if (gsAfter == null)
{
GradientStop firstStop = gradientStops.First.Value;
gradientStops.AddLast(new GradientStop(firstStop.Offset + 1, firstStop.Color));
gradientStops.RemoveFirst();
}
} while ((gsBefore == null) || (gsAfter == null));
return (gsBefore, gsAfter);
}
offset = ClipOffset(offset);
return (gradientStops.Last(n => n.Offset <= offset), gradientStops.First(n => n.Offset >= offset));
}
#endregion
}
}

View File

@ -1,108 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using System;
using RGB.NET.Core;
namespace RGB.NET.Brushes.Gradients
{
/// <inheritdoc cref="AbstractDecoratable{T}" />
/// <inheritdoc cref="IGradient" />
/// <summary>
/// Represents a rainbow gradient which circles through all colors of the HUE-color-space.<br />
/// See <see href="http://upload.wikimedia.org/wikipedia/commons/a/ad/HueScale.svg" /> as reference.
/// </summary>
public class RainbowGradient : AbstractDecoratable<IGradientDecorator>, IGradient
{
#region Properties & Fields
private double _startHue;
/// <summary>
/// Gets or sets the hue (in degrees) to start from.
/// </summary>
public double StartHue
{
get => _startHue;
set => SetProperty(ref _startHue, value);
}
private double _endHue;
/// <summary>
/// Gets or sets the hue (in degrees) to end the with.
/// </summary>
public double EndHue
{
get => _endHue;
set => SetProperty(ref _endHue, value);
}
#endregion
#region Events
/// <inheritdoc />
public event EventHandler GradientChanged;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="RainbowGradient"/> class.
/// </summary>
/// <param name="startHue">The hue (in degrees) to start from (default: 0)</param>
/// <param name="endHue">The hue (in degrees) to end with (default: 360)</param>
public RainbowGradient(double startHue = 0, double endHue = 360)
{
this.StartHue = startHue;
this.EndHue = endHue;
PropertyChanged += (sender, args) => OnGradientChanged();
}
#endregion
#region Methods
/// <inheritdoc />
/// <summary>
/// Gets the color on the rainbow at the given offset.
/// </summary>
/// <param name="offset">The percentage offset to take the color from.</param>
/// <returns>The color at the specific offset.</returns>
public Color GetColor(double offset)
{
double range = EndHue - StartHue;
double hue = StartHue + (range * offset);
return HSVColor.Create(hue, 1, 1);
}
/// <inheritdoc />
public void Move(double offset)
{
// RainbowGradient is calculated inverse
offset *= -1;
StartHue += offset;
EndHue += offset;
while ((StartHue > 360) && (EndHue > 360))
{
StartHue -= 360;
EndHue -= 360;
}
while ((StartHue < -360) && (EndHue < -360))
{
StartHue += 360;
EndHue += 360;
}
}
/// <summary>
/// Should be called to indicate that the gradient was changed.
/// </summary>
protected void OnGradientChanged() => GradientChanged?.Invoke(this, null);
#endregion
}
}

View File

@ -1,81 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
using System;
using RGB.NET.Core;
namespace RGB.NET.Brushes.Helper
{
/// <summary>
/// Offers some extensions and helper-methods for gradient related things.
/// </summary>
public static class GradientHelper
{
#region Methods
// Based on https://web.archive.org/web/20170125201230/https://dotupdate.wordpress.com/2008/01/28/find-the-color-of-a-point-in-a-lineargradientbrush/
/// <summary>
/// Calculates the offset of an given <see cref="Point"/> on an gradient.
/// </summary>
/// <param name="startPoint">The start <see cref="Point"/> of the gradient.</param>
/// <param name="endPoint">The end <see cref="Point"/> of the gradient.</param>
/// <param name="point">The <see cref="Point"/> on the gradient to which the offset is calculated.</param>
/// <returns>The offset of the <see cref="Point"/> on the gradient.</returns>
public static double CalculateLinearGradientOffset(Point startPoint, Point endPoint, Point point)
{
Point intersectingPoint;
if (startPoint.Y.Equals(endPoint.Y)) // Horizontal case
intersectingPoint = new Point(point.X, startPoint.Y);
else if (startPoint.X.Equals(endPoint.X)) // Vertical case
intersectingPoint = new Point(startPoint.X, point.Y);
else // Diagonal case
{
double slope = (endPoint.Y - startPoint.Y) / (endPoint.X - startPoint.X);
double orthogonalSlope = -1 / slope;
double startYIntercept = startPoint.Y - (slope * startPoint.X);
double pointYIntercept = point.Y - (orthogonalSlope * point.X);
double intersectingPointX = (pointYIntercept - startYIntercept) / (slope - orthogonalSlope);
double intersectingPointY = (slope * intersectingPointX) + startYIntercept;
intersectingPoint = new Point(intersectingPointX, intersectingPointY);
}
// Calculate distances relative to the vector start
double intersectDistance = CalculateDistance(intersectingPoint, startPoint, endPoint);
double gradientLength = CalculateDistance(endPoint, startPoint, endPoint);
return intersectDistance / gradientLength;
}
// Based on https://web.archive.org/web/20170125201230/https://dotupdate.wordpress.com/2008/01/28/find-the-color-of-a-point-in-a-lineargradientbrush/
/// <summary>
/// Returns the signed magnitude of a <see cref="Point"/> on a vector.
/// </summary>
/// <param name="point">The <see cref="Point"/> on the vector of which the magnitude should be calculated.</param>
/// <param name="origin">The origin of the vector.</param>
/// <param name="direction">The direction of the vector.</param>
/// <returns>The signed magnitude of a <see cref="Point"/> on a vector.</returns>
public static double CalculateDistance(Point point, Point origin, Point direction)
{
double distance = CalculateDistance(point, origin);
return (((point.Y < origin.Y) && (direction.Y > origin.Y)) ||
((point.Y > origin.Y) && (direction.Y < origin.Y)) ||
((point.Y.Equals(origin.Y)) && (point.X < origin.X) && (direction.X > origin.X)) ||
((point.Y.Equals(origin.Y)) && (point.X > origin.X) && (direction.X < origin.X)))
? -distance : distance;
}
/// <summary>
/// Calculated the distance between two <see cref="Point"/>.
/// </summary>
/// <param name="point1">The first <see cref="Point"/>.</param>
/// <param name="point2">The second <see cref="Point"/>.</param>
/// <returns>The distance between the two <see cref="Point"/>.</returns>
public static double CalculateDistance(Point point1, Point point2) => Math.Sqrt(((point1.Y - point2.Y) * (point1.Y - point2.Y)) + ((point1.X - point2.X) * (point1.X - point2.X)));
#endregion
}
}

View File

@ -1,68 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
<RuntimeIdentifiers>win7-x86;win7-x64</RuntimeIdentifiers>
<Authors>Darth Affe</Authors>
<Company>Wyrez</Company>
<Language>en-US</Language>
<NeutralLanguage>en-US</NeutralLanguage>
<Title>RGB.NET.Brushes</Title>
<AssemblyName>RGB.NET.Brushes</AssemblyName>
<AssemblyTitle>RGB.NET.Brushes</AssemblyTitle>
<PackageId>RGB.NET.Brushes</PackageId>
<RootNamespace>RGB.NET.Brushes</RootNamespace>
<Description>Brushes-Presets of RGB.NET</Description>
<Summary>Brushes-Presets of RGB.NET, a C# (.NET) library for accessing various RGB-peripherals</Summary>
<Copyright>Copyright © Darth Affe 2020</Copyright>
<PackageCopyright>Copyright © Darth Affe 2020</PackageCopyright>
<PackageIconUrl>http://lib.arge.be/icon.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/DarthAffe/RGB.NET</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/DarthAffe/RGB.NET/master/LICENSE</PackageLicenseUrl>
<RepositoryType>Github</RepositoryType>
<RepositoryUrl>https://github.com/DarthAffe/RGB.NET</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageReleaseNotes></PackageReleaseNotes>
<Version>0.0.1</Version>
<AssemblyVersion>0.0.1</AssemblyVersion>
<FileVersion>0.0.1</FileVersion>
<OutputPath>..\bin\</OutputPath>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<DefineConstants>NETCORE;NETSTANDARD;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net45'">
<DefineConstants>NET45;NETFULL</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RGB.NET.Core\RGB.NET.Core.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
</Project>

View File

@ -1,131 +0,0 @@
// ReSharper disable VirtualMemberNeverOverriden.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable VirtualMemberNeverOverridden.Global
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core
{
/// <inheritdoc cref="AbstractDecoratable{T}" />
/// <inheritdoc cref="IBrush" />
/// <summary>
/// Represents a basic brush.
/// </summary>
public abstract class AbstractBrush : AbstractDecoratable<IBrushDecorator>, IBrush
{
#region Properties & Fields
/// <inheritdoc />
public bool IsEnabled { get; set; } = true;
/// <inheritdoc />
public BrushCalculationMode BrushCalculationMode { get; set; } = BrushCalculationMode.Relative;
/// <inheritdoc />
public double Brightness { get; set; }
/// <inheritdoc />
public double Opacity { get; set; }
/// <inheritdoc />
public IList<IColorCorrection> ColorCorrections { get; } = new List<IColorCorrection>();
/// <inheritdoc />
public Rectangle RenderedRectangle { get; protected set; }
/// <inheritdoc />
public Dictionary<BrushRenderTarget, Color> RenderedTargets { get; } = new Dictionary<BrushRenderTarget, Color>();
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractBrush"/> class.
/// </summary>
/// <param name="brightness">The overall percentage brightness of the brush. (default: 1.0)</param>
/// <param name="opacity">The overall percentage opacity of the brush. (default: 1.0)</param>
protected AbstractBrush(double brightness = 1, double opacity = 1)
{
this.Brightness = brightness;
this.Opacity = opacity;
}
#endregion
#region Methods
/// <inheritdoc />
public virtual void PerformRender(Rectangle rectangle, IEnumerable<BrushRenderTarget> renderTargets)
{
RenderedRectangle = rectangle;
RenderedTargets.Clear();
foreach (BrushRenderTarget renderTarget in renderTargets)
{
Color color = GetColorAtPoint(rectangle, renderTarget);
color = ApplyDecorators(rectangle, renderTarget, color);
RenderedTargets[renderTarget] = color;
}
}
/// <summary>
/// Applies all attached and enabled decorators to the brush.
/// </summary>
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
/// <param name="color">The <see cref="Color"/> to be modified.</param>
protected virtual Color ApplyDecorators(Rectangle rectangle, BrushRenderTarget renderTarget, Color color)
{
lock (Decorators)
foreach (IBrushDecorator decorator in Decorators)
if (decorator.IsEnabled)
color = decorator.ManipulateColor(rectangle, renderTarget, color);
return color;
}
/// <inheritdoc />
public virtual void PerformFinalize()
{
List<BrushRenderTarget> renderTargets = RenderedTargets.Keys.ToList();
foreach (BrushRenderTarget renderTarget in renderTargets)
RenderedTargets[renderTarget] = FinalizeColor(RenderedTargets[renderTarget]);
}
/// <summary>
/// Gets the color at an specific point assuming the brush is drawn into the given rectangle.
/// </summary>
/// <param name="rectangle">The rectangle in which the brush should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the color should be taken.</param>
/// <returns>The color at the specified point.</returns>
protected abstract Color GetColorAtPoint(Rectangle rectangle, BrushRenderTarget renderTarget);
/// <summary>
/// Finalizes the color by appliing the overall brightness and opacity.<br/>
/// This method should always be the last call of a <see cref="GetColorAtPoint" /> implementation.
/// </summary>
/// <param name="color">The color to finalize.</param>
/// <returns>The finalized color.</returns>
protected virtual Color FinalizeColor(Color color)
{
if (ColorCorrections.Count > 0)
foreach (IColorCorrection colorCorrection in ColorCorrections)
color = colorCorrection.ApplyTo(color);
// Since we use HSV to calculate there is no way to make a color 'brighter' than 100%
// Be carefull with the naming: Since we use HSV the correct term is 'value' but outside we call it 'brightness'
// THIS IS NOT A HSB CALCULATION!!!
if (Brightness < 1)
color = color.MultiplyHSV(value: Brightness.Clamp(0, 1));
if (Opacity < 1)
color = color.MultiplyA(Opacity.Clamp(0, 1));
return color;
}
#endregion
}
}

View File

@ -1,20 +0,0 @@
// ReSharper disable UnusedMember.Global
namespace RGB.NET.Core
{
/// <summary>
/// Contains a list of all brush calculation modes.
/// </summary>
public enum BrushCalculationMode
{
/// <summary>
/// The calculation <see cref="Rectangle"/> for <see cref="IBrush"/> will be the rectangle around the <see cref="ILedGroup"/> the <see cref="IBrush"/> is applied to.
/// </summary>
Relative,
/// <summary>
/// The calculation <see cref="Rectangle"/> for <see cref="IBrush"/> will always be the rectangle completly containing all affected <see cref="IRGBDevice"/>.
/// </summary>
Absolute
}
}

View File

@ -1,47 +0,0 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
namespace RGB.NET.Core
{
/// <summary>
/// Represents a single target of a brush render.
/// </summary>
public class BrushRenderTarget
{
#region Properties & Fields
/// <summary>
/// Gets the target-<see cref="Core.Led"/>.
/// </summary>
public Led Led { get; }
/// <summary>
/// Gets the <see cref="Core.Rectangle"/> representing the area to render the target-<see cref="Core.Led"/>.
/// </summary>
public Rectangle Rectangle { get; }
/// <summary>
/// Gets the <see cref="Core.Point"/> representing the position to render the target-<see cref="Core.Led"/>.
/// </summary>
public Point Point { get; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="BrushRenderTarget"/> class.
/// </summary>
/// <param name="led">The target-<see cref="Core.Led"/>.</param>
/// <param name="rectangle">The <see cref="Core.Rectangle"/> representing the area to render the target-<see cref="Core.Led"/>.</param>
public BrushRenderTarget(Led led, Rectangle rectangle)
{
this.Led = led;
this.Rectangle = rectangle;
this.Point = rectangle.Center;
}
#endregion
}
}

View File

@ -1,61 +0,0 @@
// ReSharper disable UnusedMemberInSuper.Global
// ReSharper disable UnusedMember.Global
// ReSharper disable ReturnTypeCanBeEnumerable.Global
using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <summary>
/// Represents a basic brush.
/// </summary>
public interface IBrush : IDecoratable<IBrushDecorator>
{
/// <summary>
/// Gets or sets if the <see cref="IBrush"/> is enabled and will be drawn on an update.
/// </summary>
bool IsEnabled { get; set; }
/// <summary>
/// Gets or sets the calculation mode used for the rectangle/points used for color-selection in brushes.
/// </summary>
BrushCalculationMode BrushCalculationMode { get; set; }
/// <summary>
/// Gets or sets the overall percentage brightness of the <see cref="IBrush"/>.
/// </summary>
double Brightness { get; set; }
/// <summary>
/// Gets or sets the overall percentage opacity of the <see cref="IBrush"/>.
/// </summary>
double Opacity { get; set; }
/// <summary>
/// Gets a list of <see cref="IColorCorrection"/> used to correct the colors of the <see cref="IBrush"/>.
/// </summary>
IList<IColorCorrection> ColorCorrections { get; }
/// <summary>
/// Gets the <see cref="RenderedRectangle"/> used in the last render pass.
/// </summary>
Rectangle RenderedRectangle { get; }
/// <summary>
/// Gets a dictionary containing all <see cref="Color"/> for <see cref="BrushRenderTarget"/> calculated in the last render pass.
/// </summary>
Dictionary<BrushRenderTarget, Color> RenderedTargets { get; }
/// <summary>
/// Performs the render pass of the <see cref="IBrush"/> and calculates the raw <see cref="Color"/> for all requested <see cref="BrushRenderTarget"/>.
/// </summary>
/// <param name="rectangle">The <see cref="Rectangle"/> in which the brush should be drawn.</param>
/// <param name="renderTargets">The <see cref="BrushRenderTarget"/> (keys/points) of which the color should be calculated.</param>
void PerformRender(Rectangle rectangle, IEnumerable<BrushRenderTarget> renderTargets);
/// <summary>
/// Performs the finalize pass of the <see cref="IBrush"/> and calculates the final <see cref="ColorCorrections"/> for all previously calculated <see cref="BrushRenderTarget"/>.
/// </summary>
void PerformFinalize();
}
}

View File

@ -1,80 +1,65 @@
namespace RGB.NET.Core
using System;
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents the default-behavior for the work with colors.
/// </summary>
public sealed class DefaultColorBehavior : IColorBehavior
{
public class DefaultColorBehavior : IColorBehavior
#region Methods
/// <summary>
/// Converts the individual byte values of this <see cref="Color"/> to a human-readable string.
/// </summary>
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. For example "[A: 255, R: 255, G: 0, B: 0]".</returns>
public string ToString(Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]";
/// <summary>
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
/// </summary>
/// <param name="color">The color to test.</param>
/// <param name="obj">The object to test.</param>
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
public bool Equals(Color color, object? obj) => obj is Color color2 && Equals(color, color2);
/// <summary>
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
/// </summary>
/// <param name="color">The first color to test.</param>
/// <param name="color2">The second color to test.</param>
/// <returns><c>true</c> if <paramref name="color2" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
public bool Equals(Color color, Color color2) => color.A.EqualsInTolerance(color2.A)
&& color.R.EqualsInTolerance(color2.R)
&& color.G.EqualsInTolerance(color2.G)
&& color.B.EqualsInTolerance(color2.B);
/// <summary>
/// Returns a hash code for this <see cref="Color" />.
/// </summary>
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
public int GetHashCode(Color color) => HashCode.Combine(color.A, color.R, color.G, color.B);
/// <summary>
/// Blends a <see cref="Color"/> over this color.
/// </summary>
/// <param name="baseColor">The <see cref="Color"/> to to blend over.</param>
/// <param name="blendColor">The <see cref="Color"/> to blend.</param>
public Color Blend(Color baseColor, Color blendColor)
{
#region Properties & Fields
if (blendColor.A.EqualsInTolerance(0)) return baseColor;
private static DefaultColorBehavior _instance = new DefaultColorBehavior();
/// <summary>
/// Gets the singleton instance of <see cref="DefaultColorBehavior"/>.
/// </summary>
public static DefaultColorBehavior Instance { get; } = _instance;
if (blendColor.A.EqualsInTolerance(1))
return blendColor;
#endregion
float resultA = (1.0f - ((1.0f - blendColor.A) * (1.0f - baseColor.A)));
float resultR = (((blendColor.R * blendColor.A) / resultA) + ((baseColor.R * baseColor.A * (1.0f - blendColor.A)) / resultA));
float resultG = (((blendColor.G * blendColor.A) / resultA) + ((baseColor.G * baseColor.A * (1.0f - blendColor.A)) / resultA));
float resultB = (((blendColor.B * blendColor.A) / resultA) + ((baseColor.B * baseColor.A * (1.0f - blendColor.A)) / resultA));
#region Constructors
private DefaultColorBehavior()
{ }
#endregion
#region Methods
/// <summary>
/// Converts the individual byte values of this <see cref="Color"/> to a human-readable string.
/// </summary>
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. For example "[A: 255, R: 255, G: 0, B: 0]".</returns>
public virtual string ToString(Color color) => $"[A: {color.GetA()}, R: {color.GetR()}, G: {color.GetG()}, B: {color.GetB()}]";
/// <summary>
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
/// </summary>
/// <param name="obj">The object to test.</param>
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
public virtual bool Equals(Color color, object obj)
{
if (!(obj is Color)) return false;
(double a, double r, double g, double b) = ((Color)obj).GetRGB();
return color.A.EqualsInTolerance(a) && color.R.EqualsInTolerance(r) && color.G.EqualsInTolerance(g) && color.B.EqualsInTolerance(b);
}
/// <summary>
/// Returns a hash code for this <see cref="Color" />.
/// </summary>
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
public virtual int GetHashCode(Color color)
{
unchecked
{
int hashCode = color.A.GetHashCode();
hashCode = (hashCode * 397) ^ color.R.GetHashCode();
hashCode = (hashCode * 397) ^ color.G.GetHashCode();
hashCode = (hashCode * 397) ^ color.B.GetHashCode();
return hashCode;
}
}
/// <summary>
/// Blends a <see cref="Color"/> over this color.
/// </summary>
/// <param name="color">The <see cref="Color"/> to blend.</param>
public virtual Color Blend(Color baseColor, Color blendColor)
{
if (blendColor.A.EqualsInTolerance(0)) return baseColor;
if (blendColor.A.EqualsInTolerance(1))
return blendColor;
double resultA = (1.0 - ((1.0 - blendColor.A) * (1.0 - baseColor.A)));
double resultR = (((blendColor.R * blendColor.A) / resultA) + ((baseColor.R * baseColor.A * (1.0 - blendColor.A)) / resultA));
double resultG = (((blendColor.G * blendColor.A) / resultA) + ((baseColor.G * baseColor.A * (1.0 - blendColor.A)) / resultA));
double resultB = (((blendColor.B * blendColor.A) / resultA) + ((baseColor.B * baseColor.A * (1.0 - blendColor.A)) / resultA));
return new Color(resultA, resultR, resultG, resultB);
}
#endregion
return new Color(resultA, resultR, resultG, resultB);
}
}
#endregion
}

View File

@ -1,13 +1,43 @@
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Represents a behavior of a color for base operations.
/// </summary>
public interface IColorBehavior
{
public interface IColorBehavior
{
string ToString(Color color);
/// <summary>
/// Converts the specified <see cref="Color"/> to a string representation.
/// </summary>
/// <param name="color">The color to convert.</param>
/// <returns>The string representation of the specified color.</returns>
string ToString(Color color);
bool Equals(Color color, object obj);
/// <summary>
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
/// </summary>
/// <param name="color">The color to test.</param>
/// <param name="obj">The object to test.</param>
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
bool Equals(Color color, object? obj);
int GetHashCode(Color color);
/// <summary>
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />.
/// </summary>
/// <param name="color">The first color to test.</param>
/// <param name="color2">The second color to test.</param>
/// <returns><c>true</c> if <paramref name="color2" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
bool Equals(Color color, Color color2);
Color Blend(Color baseColor, Color blendColor);
}
}
/// <summary>
/// Returns a hash code for this <see cref="Color" />.
/// </summary>
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
int GetHashCode(Color color);
/// <summary>
/// Blends a <see cref="Color"/> over this color.
/// </summary>
/// <param name="baseColor">The <see cref="Color"/> to to blend over.</param>
/// <param name="blendColor">The <see cref="Color"/> to blend.</param>
Color Blend(Color baseColor, Color blendColor);
}

View File

@ -5,284 +5,286 @@
using System;
using System.Diagnostics;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Represents an ARGB (alpha, red, green, blue) color.
/// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
public readonly struct Color : IEquatable<Color>
{
#region Constants
private static readonly Color TRANSPARENT = new(0, 0, 0, 0);
/// <summary>
/// Gets an transparent color [A: 0, R: 0, G: 0, B: 0]
/// </summary>
public static ref readonly Color Transparent => ref TRANSPARENT;
#endregion
#region Properties & Fields
/// <summary>
/// Gets or sets the <see cref="IColorBehavior"/> used to perform operations on colors.
/// </summary>
public static IColorBehavior Behavior { get; set; } = new DefaultColorBehavior();
/// <summary>
/// Gets the alpha component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public readonly float A;
/// <summary>
/// Gets the red component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public readonly float R;
/// <summary>
/// Gets the green component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public readonly float G;
/// <summary>
/// Gets the blue component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public readonly float B;
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Represents an ARGB (alpha, red, green, blue) color.
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using RGB-Values.
/// Alpha defaults to 255.
/// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
public struct Color
/// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
/// <param name="g">The green component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
/// <param name="b">The blue component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
public Color(byte r, byte g, byte b)
: this(byte.MaxValue, r, g, b)
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using RGB-Values.
/// Alpha defaults to 255.
/// </summary>
/// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
/// <param name="g">The green component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
/// <param name="b">The blue component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
public Color(int r, int g, int b)
: this((byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(byte a, byte r, byte g, byte b)
: this(a.GetPercentageFromByteValue(), r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(int a, int r, int g, int b)
: this((byte)a.Clamp(0, byte.MaxValue), (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using RGB-percent values.
/// Alpha defaults to 1.0.
/// </summary>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(float r, float g, float b)
: this(1.0f, r, g, b)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(float a, byte r, byte g, byte b)
: this(a, r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(float a, int r, int g, int b)
: this(a, (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(int a, float r, float g, float b)
: this((byte)a.Clamp(0, byte.MaxValue), r, g, b)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(byte a, float r, float g, float b)
: this(a.GetPercentageFromByteValue(), r, g, b)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(float a, float r, float g, float b)
{
#region Constants
/// <summary>
/// Gets an transparent color [A: 0, R: 0, G: 0, B: 0]
/// </summary>
public static Color Transparent => new Color(0, 0, 0, 0);
#endregion
#region Properties & Fields
private static IColorBehavior _behavior = DefaultColorBehavior.Instance;
/// <summary>
/// Gets or sets the <see cref="IColorBehavior"/> used to perform operations on colors.
/// </summary>
public static IColorBehavior Behavior
{
get => _behavior;
set => _behavior = value ?? DefaultColorBehavior.Instance;
}
/// <summary>
/// Gets the alpha component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public double A { get; }
/// <summary>
/// Gets the red component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public double R { get; }
/// <summary>
/// Gets the green component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public double G { get; }
/// <summary>
/// Gets the blue component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
public double B { get; }
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using RGB-Values.
/// Alpha defaults to 255.
/// </summary>
/// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
/// <param name="g">The green component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
/// <param name="b">The blue component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
public Color(byte r, byte g, byte b)
: this(byte.MaxValue, r, g, b)
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using RGB-Values.
/// Alpha defaults to 255.
/// </summary>
/// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
/// <param name="g">The green component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
/// <param name="b">The blue component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
public Color(int r, int g, int b)
: this((byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(byte a, byte r, byte g, byte b)
: this(a.GetPercentageFromByteValue(), r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(int a, int r, int g, int b)
: this((byte)a.Clamp(0, byte.MaxValue), (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using RGB-percent values.
/// Alpha defaults to 1.0.
/// </summary>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(double r, double g, double b)
: this(1.0, r, g, b)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(double a, byte r, byte g, byte b)
: this(a, r.GetPercentageFromByteValue(), g.GetPercentageFromByteValue(), b.GetPercentageFromByteValue())
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(double a, int r, int g, int b)
: this(a, (byte)r.Clamp(0, byte.MaxValue), (byte)g.Clamp(0, byte.MaxValue), (byte)b.Clamp(0, byte.MaxValue))
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(int a, double r, double g, double b)
: this((byte)a.Clamp(0, byte.MaxValue), r, g, b)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(byte a, double r, double g, double b)
: this(a.GetPercentageFromByteValue(), r, g, b)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct using ARGB-percent values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="r">The red component value of this <see cref="Color"/>.</param>
/// <param name="g">The green component value of this <see cref="Color"/>.</param>
/// <param name="b">The blue component value of this <see cref="Color"/>.</param>
public Color(double a, double r, double g, double b)
{
A = a.Clamp(0, 1);
R = r.Clamp(0, 1);
G = g.Clamp(0, 1);
B = b.Clamp(0, 1);
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct by cloning a existing <see cref="T:RGB.NET.Core.Color" />.
/// </summary>
/// <param name="color">The <see cref="T:RGB.NET.Core.Color" /> the values are copied from.</param>
public Color(Color color)
: this(color.A, color.R, color.G, color.B)
{ }
#endregion
#region Methods
/// <summary>
/// Gets a human-readable string, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. Default format: "[A: 255, R: 255, G: 0, B: 0]".</returns>
public override string ToString() => Behavior.ToString(this);
/// <summary>
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <param name="obj">The object to test.</param>
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj) => Behavior.Equals(this, obj);
/// <summary>
/// Returns a hash code for this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
public override int GetHashCode() => Behavior.GetHashCode(this);
/// <summary>
/// Blends a <see cref="Color"/> over this color, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <param name="color">The <see cref="Color"/> to blend.</param>
public Color Blend(Color color) => Behavior.Blend(this, color);
#endregion
#region Operators
/// <summary>
/// Blends the provided colors as if <see cref="Blend"/> would've been called on <paramref name="color1" />.
/// </summary>
/// <param name="color1">The base color.</param>
/// <param name="color2">The color to blend.</param>
/// <returns>The blended color.</returns>
public static Color operator +(Color color1, Color color2) => color1.Blend(color2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
/// </summary>
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are equal; otherwise, <c>false</c>.</returns>
public static bool operator ==(Color color1, Color color2) => color1.Equals(color2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
/// </summary>
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are not equal; otherwise, <c>false</c>.</returns>
public static bool operator !=(Color color1, Color color2) => !(color1 == color2);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((byte r, byte g, byte b) components) => new Color(components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((byte a, byte r, byte g, byte b) components) => new Color(components.a, components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((int r, int g, int b) components) => new Color(components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((int a, int r, int g, int b) components) => new Color(components.a, components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((double r, double g, double b) components) => new Color(components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((double a, double r, double g, double b) components) => new Color(components.a, components.r, components.g, components.b);
#endregion
A = a.Clamp(0, 1);
R = r.Clamp(0, 1);
G = g.Clamp(0, 1);
B = b.Clamp(0, 1);
}
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct by cloning a existing <see cref="T:RGB.NET.Core.Color" />.
/// </summary>
/// <param name="color">The <see cref="T:RGB.NET.Core.Color" /> the values are copied from.</param>
public Color(Color color)
: this(color.A, color.R, color.G, color.B)
{ }
#endregion
#region Methods
/// <summary>
/// Gets a human-readable string, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <returns>A string that contains the individual byte values of this <see cref="Color"/>. Default format: "[A: 255, R: 255, G: 0, B: 0]".</returns>
public override string ToString() => Behavior.ToString(this);
/// <summary>
/// Tests whether the specified object is a <see cref="Color" /> and is equivalent to this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <param name="obj">The object to test.</param>
/// <returns><c>true</c> if <paramref name="obj" /> is a <see cref="Color" /> equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
public override bool Equals(object? obj) => Behavior.Equals(this, obj);
/// <summary>
/// Tests whether the specified <see cref="Color" /> is equivalent to this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <param name="other">The color to test.</param>
/// <returns><c>true</c> if <paramref name="other" /> is equivalent to this <see cref="Color" />; otherwise, <c>false</c>.</returns>
public bool Equals(Color other) => Behavior.Equals(this, other);
/// <summary>
/// Returns a hash code for this <see cref="Color" />, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <returns>An integer value that specifies the hash code for this <see cref="Color" />.</returns>
// ReSharper disable once NonReadonlyMemberInGetHashCode
public override int GetHashCode() => Behavior.GetHashCode(this);
/// <summary>
/// Blends a <see cref="Color"/> over this color, as defined by the current <see cref="Behavior"/>.
/// </summary>
/// <param name="color">The <see cref="Color"/> to blend.</param>
public Color Blend(Color color) => Behavior.Blend(this, color);
#endregion
#region Operators
/// <summary>
/// Blends the provided colors as if <see cref="Blend"/> would've been called on <paramref name="color1" />.
/// </summary>
/// <param name="color1">The base color.</param>
/// <param name="color2">The color to blend.</param>
/// <returns>The blended color.</returns>
public static Color operator +(Color color1, Color color2) => color1.Blend(color2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
/// </summary>
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are equal; otherwise, <c>false</c>.</returns>
public static bool operator ==(Color color1, Color color2) => color1.Equals(color2);
/// <summary>
/// Returns a value that indicates whether two specified <see cref="Color" /> are equal.
/// </summary>
/// <param name="color1">The first <see cref="Color" /> to compare.</param>
/// <param name="color2">The second <see cref="Color" /> to compare.</param>
/// <returns><c>true</c> if <paramref name="color1" /> and <paramref name="color2" /> are not equal; otherwise, <c>false</c>.</returns>
public static bool operator !=(Color color1, Color color2) => !(color1 == color2);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((byte r, byte g, byte b) components) => new(components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((byte a, byte r, byte g, byte b) components) => new(components.a, components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((int r, int g, int b) components) => new(components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((int a, int r, int g, int b) components) => new(components.a, components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((float r, float g, float b) components) => new(components.r, components.g, components.b);
/// <summary>
/// Converts a <see cref="ValueTuple"/> of ARGB-components to a <see cref="Color"/>.
/// </summary>
/// <param name="components">The <see cref="ValueTuple"/> containing the components.</param>
/// <returns>The color.</returns>
public static implicit operator Color((float a, float r, float g, float b) components) => new(components.a, components.r, components.g, components.b);
#endregion
}

View File

@ -2,226 +2,227 @@
// ReSharper disable UnusedMember.Global
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the HSV color space.
/// </summary>
public static class HSVColor
{
public static class HSVColor
#region Getter
/// <summary>
/// Gets the hue component value (HSV-color space) of this <see cref="Color"/> as degree in the range [0..360].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The hue component value of the color.</returns>
public static float GetHue(this Color color) => color.GetHSV().hue;
/// <summary>
/// Gets the saturation component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The saturation component value of the color.</returns>
public static float GetSaturation(this Color color) => color.GetHSV().saturation;
/// <summary>
/// Gets the value component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The value component value of the color.</returns>
public static float GetValue(this Color color) => color.GetHSV().value;
/// <summary>
/// Gets the hue, saturation and value component values (HSV-color space) of this <see cref="Color"/>.
/// Hue as degree in the range [0..1].
/// Saturation in the range [0..1].
/// Value in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>A tuple containing the hue, saturation and value component value of the color.</returns>
public static (float hue, float saturation, float value) GetHSV(this Color color)
=> CaclulateHSVFromRGB(color.R, color.G, color.B);
#endregion
#region Manipulation
/// <summary>
/// Adds the specified HSV values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="hue">The hue value to add.</param>
/// <param name="saturation">The saturation value to add.</param>
/// <param name="value">The value value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddHSV(this Color color, float hue = 0, float saturation = 0, float value = 0)
{
#region Getter
/// <summary>
/// Gets the hue component value (HSV-color space) of this <see cref="Color"/> as degree in the range [0..360].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static double GetHue(this Color color) => color.GetHSV().hue;
/// <summary>
/// Gets the saturation component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static double GetSaturation(this Color color) => color.GetHSV().saturation;
/// <summary>
/// Gets the value component value (HSV-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static double GetValue(this Color color) => color.GetHSV().value;
/// <summary>
/// Gets the hue, saturation and value component values (HSV-color space) of this <see cref="Color"/>.
/// Hue as degree in the range [0..1].
/// Saturation in the range [0..1].
/// Value in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (double hue, double saturation, double value) GetHSV(this Color color)
=> CaclulateHSVFromRGB(color.R, color.G, color.B);
#endregion
#region Manipulation
/// <summary>
/// Adds the given HSV values to this color.
/// </summary>
/// <param name="hue">The hue value to add.</param>
/// <param name="saturation">The saturation value to add.</param>
/// <param name="value">The value value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddHSV(this Color color, double hue = 0, double saturation = 0, double value = 0)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
return Create(color.A, cHue + hue, cSaturation + saturation, cValue + value);
}
/// <summary>
/// Subtracts the given HSV values to this color.
/// </summary>
/// <param name="hue">The hue value to subtract.</param>
/// <param name="saturation">The saturation value to subtract.</param>
/// <param name="value">The value value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractHSV(this Color color, double hue = 0, double saturation = 0, double value = 0)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
return Create(color.A, cHue - hue, cSaturation - saturation, cValue - value);
}
/// <summary>
/// Multiplies the given HSV values to this color.
/// </summary>
/// <param name="hue">The hue value to multiply.</param>
/// <param name="saturation">The saturation value to multiply.</param>
/// <param name="value">The value value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyHSV(this Color color, double hue = 1, double saturation = 1, double value = 1)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
return Create(color.A, cHue * hue, cSaturation * saturation, cValue * value);
}
/// <summary>
/// Divides the given HSV values to this color.
/// </summary>
/// <param name="hue">The hue value to divide.</param>
/// <param name="saturation">The saturation value to divide.</param>
/// <param name="value">The value value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideHSV(this Color color, double hue = 1, double saturation = 1, double value = 1)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
return Create(color.A, cHue / hue, cSaturation / saturation, cValue / value);
}
/// <summary>
/// Sets the given hue value of this color.
/// </summary>
/// <param name="hue">The hue value to set.</param>
/// <param name="saturation">The saturation value to set.</param>
/// <param name="value">The value value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetHSV(this Color color, double? hue = null, double? saturation = null, double? value = null)
{
(double cHue, double cSaturation, double cValue) = color.GetHSV();
return Create(color.A, hue ?? cHue, saturation ?? cSaturation, value ?? cValue);
}
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using HSV-Values.
/// </summary>
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(double hue, double saturation, double value)
=> Create(1.0, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte a, double hue, double saturation, double value)
=> Create((double)a / byte.MaxValue, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int a, double hue, double saturation, double value)
=> Create((double)a / byte.MaxValue, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(double a, double hue, double saturation, double value)
{
(double r, double g, double b) = CalculateRGBFromHSV(hue, saturation, value);
return new Color(a, r, g, b);
}
#endregion
#region Helper
private static (double h, double s, double v) CaclulateHSVFromRGB(double r, double g, double b)
{
if (r.EqualsInTolerance(g) && g.EqualsInTolerance(b)) return (0, 0, r);
double min = Math.Min(Math.Min(r, g), b);
double max = Math.Max(Math.Max(r, g), b);
double hue;
if (max.EqualsInTolerance(min))
hue = 0;
else if (max.EqualsInTolerance(r)) // r is max
hue = (g - b) / (max - min);
else if (max.EqualsInTolerance(g)) // g is max
hue = 2.0 + ((b - r) / (max - min));
else // b is max
hue = 4.0 + ((r - g) / (max - min));
hue = hue * 60.0;
hue = hue.Wrap(0, 360);
double saturation = max.EqualsInTolerance(0) ? 0 : 1.0 - (min / max);
double value = Math.Max(r, Math.Max(g, b));
return (hue, saturation, value);
}
private static (double r, double g, double b) CalculateRGBFromHSV(double h, double s, double v)
{
h = h.Wrap(0, 360);
s = s.Clamp(0, 1);
v = v.Clamp(0, 1);
if (s <= 0.0)
return (v, v, v);
double hh = h / 60.0;
int i = (int)hh;
double ff = hh - i;
double p = v * (1.0 - s);
double q = v * (1.0 - (s * ff));
double t = v * (1.0 - (s * (1.0 - ff)));
switch (i)
{
case 0:
return (v, t, p);
case 1:
return (q, v, p);
case 2:
return (p, v, t);
case 3:
return (p, q, v);
case 4:
return (t, p, v);
default:
return (v, p, q);
}
}
#endregion
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue + hue, cSaturation + saturation, cValue + value);
}
}
/// <summary>
/// Subtracts the specified HSV values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="hue">The hue value to subtract.</param>
/// <param name="saturation">The saturation value to subtract.</param>
/// <param name="value">The value value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractHSV(this Color color, float hue = 0, float saturation = 0, float value = 0)
{
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue - hue, cSaturation - saturation, cValue - value);
}
/// <summary>
/// Multiplies the specified HSV values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="hue">The hue value to multiply.</param>
/// <param name="saturation">The saturation value to multiply.</param>
/// <param name="value">The value value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyHSV(this Color color, float hue = 1, float saturation = 1, float value = 1)
{
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue * hue, cSaturation * saturation, cValue * value);
}
/// <summary>
/// Divides the specified HSV values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="hue">The hue value to divide.</param>
/// <param name="saturation">The saturation value to divide.</param>
/// <param name="value">The value value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideHSV(this Color color, float hue = 1, float saturation = 1, float value = 1)
{
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue / hue, cSaturation / saturation, cValue / value);
}
/// <summary>
/// Sets the specified hue value of this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="hue">The hue value to set.</param>
/// <param name="saturation">The saturation value to set.</param>
/// <param name="value">The value value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetHSV(this Color color, float? hue = null, float? saturation = null, float? value = null)
{
(float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, hue ?? cHue, saturation ?? cSaturation, value ?? cValue);
}
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using HSV-Values.
/// </summary>
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float hue, float saturation, float value)
=> Create(1.0f, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte a, float hue, float saturation, float value)
=> Create((float)a / byte.MaxValue, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int a, float hue, float saturation, float value)
=> Create((float)a / byte.MaxValue, hue, saturation, value);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using AHSV-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="hue">The hue component value of this <see cref="Color"/>.</param>
/// <param name="saturation">The saturation component value of this <see cref="Color"/>.</param>
/// <param name="value">The value component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float a, float hue, float saturation, float value)
{
(float r, float g, float b) = CalculateRGBFromHSV(hue, saturation, value);
return new Color(a, r, g, b);
}
#endregion
#region Helper
private static (float h, float s, float v) CaclulateHSVFromRGB(float r, float g, float b)
{
if (r.EqualsInTolerance(g) && g.EqualsInTolerance(b)) return (0, 0, r);
float min = Math.Min(Math.Min(r, g), b);
float max = Math.Max(Math.Max(r, g), b);
float hue;
if (max.EqualsInTolerance(min))
hue = 0;
else if (max.EqualsInTolerance(r)) // r is max
hue = (g - b) / (max - min);
else if (max.EqualsInTolerance(g)) // g is max
hue = 2.0f + ((b - r) / (max - min));
else // b is max
hue = 4.0f + ((r - g) / (max - min));
hue *= 60.0f;
hue = hue.Wrap(0, 360);
float saturation = max.EqualsInTolerance(0) ? 0 : 1.0f - (min / max);
float value = Math.Max(r, Math.Max(g, b));
return (hue, saturation, value);
}
private static (float r, float g, float b) CalculateRGBFromHSV(float h, float s, float v)
{
h = h.Wrap(0, 360);
s = s.Clamp(0, 1);
v = v.Clamp(0, 1);
if (s <= 0.0)
return (v, v, v);
float hh = h / 60.0f;
int i = (int)hh;
float ff = hh - i;
float p = v * (1.0f - s);
float q = v * (1.0f - (s * ff));
float t = v * (1.0f - (s * (1.0f - ff)));
return i switch
{
0 => (v, t, p),
1 => (q, v, p),
2 => (p, v, t),
3 => (p, q, v),
4 => (t, p, v),
_ => (v, p, q)
};
}
#endregion
}

View File

@ -0,0 +1,211 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
namespace RGB.NET.Core;
/// <summary>
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the Hcl color space.
/// </summary>
public static class HclColor
{
#region Getter
/// <summary>
/// Gets the H component value (Hcl-color space) of this <see cref="Color"/> in the range [0..360].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The H component value of the color. </returns>
public static float GetHclH(this Color color) => color.GetHcl().h;
/// <summary>
/// Gets the c component value (Hcl-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The c component value of the color. </returns>
public static float GetHclC(this Color color) => color.GetHcl().c;
/// <summary>
/// Gets the l component value (Hcl-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The l component value of the color. </returns>
public static float GetHclL(this Color color) => color.GetHcl().l;
/// <summary>
/// Gets the H, c and l component values (Hcl-color space) of this <see cref="Color"/>.
/// H in the range [0..360].
/// c in the range [0..1].
/// l in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>A tuple containing the H, c and l component value of the color.</returns>
public static (float h, float c, float l) GetHcl(this Color color)
=> CalculateHclFromRGB(color.R, color.G, color.B);
#endregion
#region Manipulation
/// <summary>
/// Adds the specified Hcl values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="h">The H value to add.</param>
/// <param name="c">The c value to add.</param>
/// <param name="l">The l value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddHcl(this Color color, float h = 0, float c = 0, float l = 0)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, cH + h, cC + c, cL + l);
}
/// <summary>
/// Subtracts the specified Hcl values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="h">The H value to subtract.</param>
/// <param name="c">The c value to subtract.</param>
/// <param name="l">The l value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractHcl(this Color color, float h = 0, float c = 0, float l = 0)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, cH - h, cC - c, cL - l);
}
/// <summary>
/// Multiplies the specified Hcl values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="h">The H value to multiply.</param>
/// <param name="c">The c value to multiply.</param>
/// <param name="l">The l value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyHcl(this Color color, float h = 1, float c = 1, float l = 1)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, cH * h, cC * c, cL * l);
}
/// <summary>
/// Divides the specified Hcl values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="h">The H value to divide.</param>
/// <param name="c">The c value to divide.</param>
/// <param name="l">The l value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideHcl(this Color color, float h = 1, float c = 1, float l = 1)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, cH / h, cC / c, cL / l);
}
/// <summary>
/// Sets the specified X value of this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="h">The H value to set.</param>
/// <param name="c">The c value to set.</param>
/// <param name="l">The l value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetHcl(this Color color, float? h = null, float? c = null, float? l = null)
{
(float cH, float cC, float cL) = color.GetHcl();
return Create(color.A, h ?? cH, c ?? cC, l ?? cL);
}
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using Hcl-Values.
/// </summary>
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float h, float c, float l)
=> Create(1.0f, h, c, l);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
/// </summary>
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte alpha, float h, float c, float l)
=> Create((float)alpha / byte.MaxValue, h, c, l);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
/// </summary>
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int alpha, float h, float c, float l)
=> Create((float)alpha / byte.MaxValue, h, c, l);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Hcl-Values.
/// </summary>
/// <param name="alpha">The alphc component value of this <see cref="Color"/>.</param>
/// <param name="h">The H component value of this <see cref="Color"/>.</param>
/// <param name="c">The c component value of this <see cref="Color"/>.</param>
/// <param name="l">The l component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float alpha, float h, float c, float l)
{
(float r, float g, float b) = CalculateRGBFromHcl(h, c, l);
return new Color(alpha, r, g, b);
}
#endregion
#region Helper
private static (float h, float c, float l) CalculateHclFromRGB(float r, float g, float b)
{
const float RADIANS_DEGREES_CONVERSION = 180.0f / MathF.PI;
// ReSharper disable once InconsistentNaming - b is used above
(float l, float a, float _b) = LabColor.CalculateLabFromRGB(r, g, b);
float h, c;
if (r.EqualsInTolerance(g) && r.EqualsInTolerance(b)) //DarthAffe 26.02.2021: The cumulated rounding errors are big enough to cause problems in that case
{
h = 0;
c = 0;
}
else
{
h = MathF.Atan2(_b, a);
if (h >= 0) h *= RADIANS_DEGREES_CONVERSION;
else h = 360 - (-h * RADIANS_DEGREES_CONVERSION);
c = MathF.Sqrt((a * a) + (_b * _b));
}
return (h, c, l);
}
private static (float r, float g, float b) CalculateRGBFromHcl(float h, float c, float l)
{
const float DEGREES_RADIANS_CONVERSION = MathF.PI / 180.0f;
h *= DEGREES_RADIANS_CONVERSION;
float a = c * MathF.Cos(h);
float b = c * MathF.Sin(h);
return LabColor.CalculateRGBFromLab(l, a, b);
}
#endregion
}

View File

@ -0,0 +1,227 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
namespace RGB.NET.Core;
/// <summary>
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the Lab color space.
/// </summary>
public static class LabColor
{
#region Getter
/// <summary>
/// Gets the L component value (Lab-color space) of this <see cref="Color"/> in the range [0..100].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The L component value of the color.</returns>
public static float GetLabL(this Color color) => color.GetLab().l;
/// <summary>
/// Gets the a component value (Lab-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The a component value of the color.</returns>
public static float GetLabA(this Color color) => color.GetLab().a;
/// <summary>
/// Gets the b component value (Lab-color space) of this <see cref="Color"/> in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The b component value of the color.</returns>
public static float GetLabB(this Color color) => color.GetLab().b;
/// <summary>
/// Gets the L, a and b component values (Lab-color space) of this <see cref="Color"/>.
/// L in the range [0..100].
/// a in the range [0..1].
/// b in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>A tuple containing the L, a and b component value of the color.</returns>
public static (float l, float a, float b) GetLab(this Color color)
=> CalculateLabFromRGB(color.R, color.G, color.B);
#endregion
#region Manipulation
/// <summary>
/// Adds the specified Lab values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="l">The L value to add.</param>
/// <param name="a">The a value to add.</param>
/// <param name="b">The b value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddLab(this Color color, float l = 0, float a = 0, float b = 0)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, cL + l, cA + a, cB + b);
}
/// <summary>
/// Subtracts the specified Lab values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="l">The L value to subtract.</param>
/// <param name="a">The a value to subtract.</param>
/// <param name="b">The b value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractLab(this Color color, float l = 0, float a = 0, float b = 0)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, cL - l, cA - a, cB - b);
}
/// <summary>
/// Multiplies the specified Lab values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="l">The L value to multiply.</param>
/// <param name="a">The a value to multiply.</param>
/// <param name="b">The b value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyLab(this Color color, float l = 1, float a = 1, float b = 1)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, cL * l, cA * a, cB * b);
}
/// <summary>
/// Divides the specified Lab values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="l">The L value to divide.</param>
/// <param name="a">The a value to divide.</param>
/// <param name="b">The b value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideLab(this Color color, float l = 1, float a = 1, float b = 1)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, cL / l, cA / a, cB / b);
}
/// <summary>
/// Sets the specified X valueof this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="l">The L value to set.</param>
/// <param name="a">The a value to set.</param>
/// <param name="b">The b value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetLab(this Color color, float? l = null, float? a = null, float? b = null)
{
(float cL, float cA, float cB) = color.GetLab();
return Create(color.A, l ?? cL, a ?? cA, b ?? cB);
}
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using Lab-Values.
/// </summary>
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float l, float a, float b)
=> Create(1.0f, l, a, b);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
/// </summary>
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte alpha, float l, float a, float b)
=> Create((float)alpha / byte.MaxValue, l, a, b);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
/// </summary>
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int alpha, float l, float a, float b)
=> Create((float)alpha / byte.MaxValue, l, a, b);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and Lab-Values.
/// </summary>
/// <param name="alpha">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="l">The L component value of this <see cref="Color"/>.</param>
/// <param name="a">The a component value of this <see cref="Color"/>.</param>
/// <param name="b">The b component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float alpha, float l, float a, float b)
{
// ReSharper disable once InconsistentNaming - b is used above
(float r, float g, float _b) = CalculateRGBFromLab(l, a, b);
return new Color(alpha, r, g, _b);
}
#endregion
#region Helper
internal static (float l, float a, float b) CalculateLabFromRGB(float r, float g, float b)
{
(float x, float y, float z) = XYZColor.CaclulateXYZFromRGB(r, g, b);
return CaclulateLabFromXYZ(x, y, z);
}
internal static (float r, float g, float b) CalculateRGBFromLab(float l, float a, float b)
{
(float x, float y, float z) = CalculateXYZFromLab(l, a, b);
return XYZColor.CalculateRGBFromXYZ(x, y, z);
}
private static (float l, float a, float b) CaclulateLabFromXYZ(float x, float y, float z)
{
const float ONETHRID = 1.0f / 3.0f;
const float FACTOR2 = 16.0f / 116.0f;
x /= 95.047f;
y /= 100.0f;
z /= 108.883f;
x = ((x > 0.008856f) ? (MathF.Pow(x, ONETHRID)) : ((7.787f * x) + FACTOR2));
y = ((y > 0.008856f) ? (MathF.Pow(y, ONETHRID)) : ((7.787f * y) + FACTOR2));
z = ((z > 0.008856f) ? (MathF.Pow(z, ONETHRID)) : ((7.787f * z) + FACTOR2));
float l = (116.0f * y) - 16.0f;
float a = 500.0f * (x - y);
float b = 200.0f * (y - z);
return (l, a, b);
}
private static (float x, float y, float z) CalculateXYZFromLab(float l, float a, float b)
{
const float FACTOR2 = 16.0f / 116.0f;
float y = (l + 16.0f) / 116.0f;
float x = (a / 500.0f) + y;
float z = y - (b / 200.0f);
float powX = MathF.Pow(x, 3.0f);
float powY = MathF.Pow(y, 3.0f);
float powZ = MathF.Pow(z, 3.0f);
x = ((powX > 0.008856f) ? (powX) : ((x - FACTOR2) / 7.787f));
y = ((powY > 0.008856f) ? (powY) : ((y - FACTOR2) / 7.787f));
z = ((powZ > 0.008856f) ? (powZ) : ((z - FACTOR2) / 7.787f));
return (x * 95.047f, y * 100.0f, z * 108.883f);
}
#endregion
}

View File

@ -2,274 +2,294 @@
// ReSharper disable UnusedMember.Global
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the RGB color space.
/// </summary>
public static class RGBColor
{
public static class RGBColor
#region Getter
/// <summary>
/// Gets the A component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The A component value of the color.</returns>
public static byte GetA(this Color color) => color.A.GetByteValueFromPercentage();
/// <summary>
/// Gets the R component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The R component value of the color.</returns>
public static byte GetR(this Color color) => color.R.GetByteValueFromPercentage();
/// <summary>
/// Gets the G component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The G component value of the color.</returns>
public static byte GetG(this Color color) => color.G.GetByteValueFromPercentage();
/// <summary>
/// Gets the B component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The B component value of the color.</returns>
public static byte GetB(this Color color) => color.B.GetByteValueFromPercentage();
/// <summary>
/// Gets the A, R, G and B component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>A tuple containing the A, R, G and B component value of the color.</returns>
public static (byte a, byte r, byte g, byte b) GetRGBBytes(this Color color)
=> (color.GetA(), color.GetR(), color.GetG(), color.GetB());
/// <summary>
/// Gets the A, R, G and B component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>A tuple containing the A, R, G and B component value of the color.</returns>
public static (float a, float r, float g, float b) GetRGB(this Color color)
=> (color.A, color.R, color.G, color.B);
#endregion
#region Manipulation
#region Add
/// <summary>
/// Adds the specified RGB values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to add.</param>
/// <param name="g">The green value to add.</param>
/// <param name="b">The blue value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddRGB(this Color color, int r = 0, int g = 0, int b = 0)
=> new(color.A, color.GetR() + r, color.GetG() + g, color.GetB() + b);
/// <summary>
/// Adds the specified RGB-percent values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to add.</param>
/// <param name="g">The green value to add.</param>
/// <param name="b">The blue value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddRGB(this Color color, float r = 0, float g = 0, float b = 0)
=> new(color.A, color.R + r, color.G + g, color.B + b);
/// <summary>
/// Adds the specified alpha value to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="a">The alpha value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddA(this Color color, int a)
=> new(color.GetA() + a, color.R, color.G, color.B);
/// <summary>
/// Adds the specified alpha-percent value to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="a">The alpha value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddA(this Color color, float a)
=> new(color.A + a, color.R, color.G, color.B);
#endregion
#region Subtract
/// <summary>
/// Subtracts the specified RGB values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to subtract.</param>
/// <param name="g">The green value to subtract.</param>
/// <param name="b">The blue value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractRGB(this Color color, int r = 0, int g = 0, int b = 0)
=> new(color.A, color.GetR() - r, color.GetG() - g, color.GetB() - b);
/// <summary>
/// Subtracts the specified RGB values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to subtract.</param>
/// <param name="g">The green value to subtract.</param>
/// <param name="b">The blue value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractRGB(this Color color, float r = 0, float g = 0, float b = 0)
=> new(color.A, color.R - r, color.G - g, color.B - b);
/// <summary>
/// Subtracts the specified alpha value to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="a">The alpha value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractA(this Color color, int a)
=> new(color.GetA() - a, color.R, color.G, color.B);
/// <summary>
/// Subtracts the specified alpha-percent value to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="aPercent">The alpha value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractA(this Color color, float aPercent)
=> new(color.A - aPercent, color.R, color.G, color.B);
#endregion
#region Multiply
/// <summary>
/// Multiplies the specified RGB values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to multiply.</param>
/// <param name="g">The green value to multiply.</param>
/// <param name="b">The blue value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyRGB(this Color color, float r = 1, float g = 1, float b = 1)
=> new(color.A, color.R * r, color.G * g, color.B * b);
/// <summary>
/// Multiplies the specified alpha value to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="a">The alpha value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyA(this Color color, float a)
=> new(color.A * a, color.R, color.G, color.B);
#endregion
#region Divide
/// <summary>
/// Divides the specified RGB values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to divide.</param>
/// <param name="g">The green value to divide.</param>
/// <param name="b">The blue value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideRGB(this Color color, float r = 1, float g = 1, float b = 1)
=> new(color.A, color.R / r, color.G / g, color.B / b);
/// <summary>
/// Divides the specified alpha value to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="a">The alpha value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideA(this Color color, float a)
=> new(color.A / a, color.R, color.G, color.B);
#endregion
#region Set
/// <summary>
/// Sets the specified RGB value of this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to set.</param>
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, byte? r = null, byte? g = null, byte? b = null)
=> new(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
/// <summary>
/// Sets the specified RGB value of this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to set.</param>
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, int? r = null, int? g = null, int? b = null)
=> new(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
/// <summary>
/// Sets the specified RGB value of this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="r">The red value to set.</param>
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, float? r = null, float? g = null, float? b = null)
=> new(color.A, r ?? color.R, g ?? color.G, b ?? color.B);
/// <summary>
/// Sets the specified alpha value of this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="a">The alpha value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetA(this Color color, int a) => new(a, color.R, color.G, color.B);
/// <summary>
/// Sets the specified alpha value of this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="a">The alpha value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetA(this Color color, float a) => new(a, color.R, color.G, color.B);
#endregion
#endregion
#region Conversion
/// <summary>
/// Gets the current color as a RGB-HEX-string.
/// </summary>
/// <returns>The RGB-HEX-string.</returns>
public static string AsRGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetR(), color.GetG(), color.GetB());
/// <summary>
/// Gets the current color as a ARGB-HEX-string.
/// </summary>
/// <returns>The ARGB-HEX-string.</returns>
public static string AsARGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetA(), color.GetR(), color.GetG(), color.GetB());
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using a HEX-string.
/// </summary>
/// <param name="hexString">The HEX-representation of the color.</param>
/// <returns>The color created from the HEX-string.</returns>
public static Color FromHexString(string hexString)
{
#region Getter
/// <summary>
/// Gets the A component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static byte GetA(this Color color) => color.A.GetByteValueFromPercentage();
/// <summary>
/// Gets the R component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static byte GetR(this Color color) => color.R.GetByteValueFromPercentage();
/// <summary>
/// Gets the G component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static byte GetG(this Color color) => color.G.GetByteValueFromPercentage();
/// <summary>
/// Gets the B component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static byte GetB(this Color color) => color.B.GetByteValueFromPercentage();
/// <summary>
/// Gets the A, R, G and B component value of this <see cref="Color"/> as byte in the range [0..255].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (byte a, byte r, byte g, byte b) GetRGBBytes(this Color color)
=> (color.GetA(), color.GetR(), color.GetG(), color.GetB());
/// <summary>
/// Gets the A, R, G and B component value of this <see cref="Color"/> as percentage in the range [0..1].
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static (double a, double r, double g, double b) GetRGB(this Color color)
=> (color.A, color.R, color.G, color.B);
#endregion
#region Manipulation
#region Add
/// <summary>
/// Adds the given RGB values to this color.
/// </summary>
/// <param name="r">The red value to add.</param>
/// <param name="g">The green value to add.</param>
/// <param name="b">The blue value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddRGB(this Color color, int r = 0, int g = 0, int b = 0)
=> new Color(color.A, color.GetR() + r, color.GetG() + g, color.GetB() + b);
/// <summary>
/// Adds the given RGB-percent values to this color.
/// </summary>
/// <param name="r">The red value to add.</param>
/// <param name="g">The green value to add.</param>
/// <param name="b">The blue value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddRGB(this Color color, double r = 0, double g = 0, double b = 0)
=> new Color(color.A, color.R + r, color.G + g, color.B + b);
/// <summary>
/// Adds the given alpha value to this color.
/// </summary>
/// <param name="a">The alpha value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddA(this Color color, int a)
=> new Color(color.GetA() + a, color.R, color.G, color.B);
/// <summary>
/// Adds the given alpha-percent value to this color.
/// </summary>
/// <param name="a">The alpha value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddA(this Color color, double a)
=> new Color(color.A + a, color.R, color.G, color.B);
#endregion
#region Subtract
/// <summary>
/// Subtracts the given RGB values to this color.
/// </summary>
/// <param name="r">The red value to subtract.</param>
/// <param name="g">The green value to subtract.</param>
/// <param name="b">The blue value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractRGB(this Color color, int r = 0, int g = 0, int b = 0)
=> new Color(color.A, color.GetR() - r, color.GetG() - g, color.GetB() - b);
/// <summary>
/// Subtracts the given RGB values to this color.
/// </summary>
/// <param name="r">The red value to subtract.</param>
/// <param name="g">The green value to subtract.</param>
/// <param name="b">The blue value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractRGB(this Color color, double r = 0, double g = 0, double b = 0)
=> new Color(color.A, color.R - r, color.G - g, color.B - b);
/// <summary>
/// Subtracts the given alpha value to this color.
/// </summary>
/// <param name="a">The alpha value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractA(this Color color, int a)
=> new Color(color.GetA() - a, color.R, color.G, color.B);
/// <summary>
/// Subtracts the given alpha-percent value to this color.
/// </summary>
/// <param name="a">The alpha value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractA(this Color color, double aPercent)
=> new Color(color.A - aPercent, color.R, color.G, color.B);
#endregion
#region Multiply
/// <summary>
/// Multiplies the given RGB values to this color.
/// </summary>
/// <param name="r">The red value to multiply.</param>
/// <param name="g">The green value to multiply.</param>
/// <param name="b">The blue value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyRGB(this Color color, double r = 1, double g = 1, double b = 1)
=> new Color(color.A, color.R * r, color.G * g, color.B * b);
/// <summary>
/// Multiplies the given alpha value to this color.
/// </summary>
/// <param name="a">The alpha value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyA(this Color color, double a)
=> new Color(color.A * a, color.R, color.G, color.B);
#endregion
#region Divide
/// <summary>
/// Divides the given RGB values to this color.
/// </summary>
/// <param name="r">The red value to divide.</param>
/// <param name="g">The green value to divide.</param>
/// <param name="b">The blue value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideRGB(this Color color, double r = 1, double g = 1, double b = 1)
=> new Color(color.A, color.R / r, color.G / g, color.B / b);
/// <summary>
/// Divides the given alpha value to this color.
/// </summary>
/// <param name="a">The alpha value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideA(this Color color, double a)
=> new Color(color.A / a, color.R, color.G, color.B);
#endregion
#region Set
/// <summary>
/// Sets the given RGB value of this color.
/// </summary>
/// <param name="r">The red value to set.</param>
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, byte? r = null, byte? g = null, byte? b = null)
=> new Color(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
/// <summary>
/// Sets the given RGB value of this color.
/// </summary>
/// <param name="r">The red value to set.</param>
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, int? r = null, int? g = null, int? b = null)
=> new Color(color.A, r ?? color.GetR(), g ?? color.GetG(), b ?? color.GetB());
/// <summary>
/// Sets the given RGB value of this color.
/// </summary>
/// <param name="r">The red value to set.</param>
/// <param name="g">The green value to set.</param>
/// <param name="b">The blue value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetRGB(this Color color, double? r = null, double? g = null, double? b = null)
=> new Color(color.A, r ?? color.R, g ?? color.G, b ?? color.B);
/// <summary>
/// Sets the given alpha value of this color.
/// </summary>
/// <param name="a">The alpha value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetA(this Color color, int a) => new Color(a, color.R, color.G, color.B);
/// <summary>
/// Sets the given alpha value of this color.
/// </summary>
/// <param name="a">The alpha value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetA(this Color color, double a) => new Color(a, color.R, color.G, color.B);
#endregion
#endregion
#region Conversion
/// <summary>
/// Gets the current color as a RGB-HEX-string.
/// </summary>
/// <returns>The RGB-HEX-string.</returns>
public static string AsRGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetR(), color.GetG(), color.GetB());
/// <summary>
/// Gets the current color as a ARGB-HEX-string.
/// </summary>
/// <returns>The ARGB-HEX-string.</returns>
public static string AsARGBHexString(this Color color, bool leadingHash = true) => (leadingHash ? "#" : "") + ConversionHelper.ToHex(color.GetA(), color.GetR(), color.GetG(), color.GetB());
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using a HEX-string.
/// </summary>
/// <param name="hexString">The HEX-representation of the color.</param>
/// <returns>The color created from the HEX-string.</returns>
public static Color FromHexString(string hexString)
{
if ((hexString == null) || (hexString.Length < 6))
throw new ArgumentException("Invalid hex string", nameof(hexString));
if (hexString[0] == '#')
hexString = hexString.Substring(1);
byte[] data = ConversionHelper.HexToBytes(hexString);
if (data.Length == 3)
return new Color(data[0], data[1], data[2]);
if (data.Length == 4)
return new Color(data[0], data[1], data[2], data[3]);
if ((hexString == null) || (hexString.Length < 6))
throw new ArgumentException("Invalid hex string", nameof(hexString));
}
#endregion
ReadOnlySpan<char> span = hexString.AsSpan();
if (span[0] == '#')
span = span[1..];
byte[] data = ConversionHelper.HexToBytes(span);
return data.Length switch
{
3 => new Color(data[0], data[1], data[2]),
4 => new Color(data[0], data[1], data[2], data[3]),
_ => throw new ArgumentException($"Invalid hex string '{hexString}'", nameof(hexString))
};
}
}
#endregion
}

View File

@ -0,0 +1,207 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
namespace RGB.NET.Core;
/// <summary>
/// Contains helper-methods and extension for the <see cref="Color"/>-type to work in the XYZ color space.
/// </summary>
public static class XYZColor
{
#region Getter
/// <summary>
/// Gets the X component value (XYZ-color space) of this <see cref="Color"/> in the range [0..95.047].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The X component value of the color.</returns>
public static float GetX(this Color color) => color.GetXYZ().x;
/// <summary>
/// Gets the Y component value (XYZ-color space) of this <see cref="Color"/> in the range [0..100].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The Y component value of the color.</returns>
public static float GetY(this Color color) => color.GetXYZ().y;
/// <summary>
/// Gets the Z component value (XYZ-color space) of this <see cref="Color"/> in the range [0..108.883].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>The Z component value of the color.</returns>
public static float GetZ(this Color color) => color.GetXYZ().z;
/// <summary>
/// Gets the X, Y and Z component values (XYZ-color space) of this <see cref="Color"/>.
/// X in the range [0..95.047].
/// Y in the range [0..100].
/// Z in the range [0..108.883].
/// </summary>
/// <param name="color">The color to get the value from.</param>
/// <returns>A tuple containing the X, Y and Z component value of the color.</returns>
public static (float x, float y, float z) GetXYZ(this Color color)
=> CaclulateXYZFromRGB(color.R, color.G, color.B);
#endregion
#region Manipulation
/// <summary>
/// Adds the specified XYZ values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="x">The X value to add.</param>
/// <param name="y">The Y value to add.</param>
/// <param name="z">The Z value to add.</param>
/// <returns>The new color after the modification.</returns>
public static Color AddXYZ(this Color color, float x = 0, float y = 0, float z = 0)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, cX + x, cY + y, cZ + z);
}
/// <summary>
/// Subtracts the specified XYZ values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="x">The X value to subtract.</param>
/// <param name="y">The Y value to subtract.</param>
/// <param name="z">The Z value to subtract.</param>
/// <returns>The new color after the modification.</returns>
public static Color SubtractXYZ(this Color color, float x = 0, float y = 0, float z = 0)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, cX - x, cY - y, cZ - z);
}
/// <summary>
/// Multiplies the specified XYZ values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="x">The X value to multiply.</param>
/// <param name="y">The Y value to multiply.</param>
/// <param name="z">The Z value to multiply.</param>
/// <returns>The new color after the modification.</returns>
public static Color MultiplyXYZ(this Color color, float x = 1, float y = 1, float z = 1)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, cX * x, cY * y, cZ * z);
}
/// <summary>
/// Divides the specified XYZ values to this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="x">The X value to divide.</param>
/// <param name="y">The Y value to divide.</param>
/// <param name="z">The Z value to divide.</param>
/// <returns>The new color after the modification.</returns>
public static Color DivideXYZ(this Color color, float x = 1, float y = 1, float z = 1)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, cX / x, cY / y, cZ / z);
}
/// <summary>
/// Sets the specified X valueof this color.
/// </summary>
/// <param name="color">The color to modify.</param>
/// <param name="x">The X value to set.</param>
/// <param name="y">The Y value to set.</param>
/// <param name="z">The Z value to set.</param>
/// <returns>The new color after the modification.</returns>
public static Color SetXYZ(this Color color, float? x = null, float? y = null, float? z = null)
{
(float cX, float cY, float cZ) = color.GetXYZ();
return Create(color.A, x ?? cX, y ?? cY, z ?? cZ);
}
#endregion
#region Factory
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using XYZ-Values.
/// </summary>
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float x, float y, float z)
=> Create(1.0f, x, y, z);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(byte a, float x, float y, float z)
=> Create((float)a / byte.MaxValue, x, y, z);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(int a, float x, float y, float z)
=> Create((float)a / byte.MaxValue, x, y, z);
/// <summary>
/// Creates a new instance of the <see cref="T:RGB.NET.Core.Color" /> struct using alpha and XYZ-Values.
/// </summary>
/// <param name="a">The alpha component value of this <see cref="Color"/>.</param>
/// <param name="x">The X component value of this <see cref="Color"/>.</param>
/// <param name="y">The Y component value of this <see cref="Color"/>.</param>
/// <param name="z">The Z component value of this <see cref="Color"/>.</param>
/// <returns>The color created from the values.</returns>
public static Color Create(float a, float x, float y, float z)
{
(float r, float g, float b) = CalculateRGBFromXYZ(x, y, z);
return new Color(a, r, g, b);
}
#endregion
#region Helper
internal static (float x, float y, float z) CaclulateXYZFromRGB(float r, float g, float b)
{
r = ((r > 0.04045f) ? MathF.Pow(((r + 0.055f) / 1.055f), 2.4f) : (r / 12.92f)) * 100.0f;
g = ((g > 0.04045f) ? MathF.Pow(((g + 0.055f) / 1.055f), 2.4f) : (g / 12.92f)) * 100.0f;
b = ((b > 0.04045f) ? MathF.Pow(((b + 0.055f) / 1.055f), 2.4f) : (b / 12.92f)) * 100.0f;
float x = (r * 0.4124f) + (g * 0.3576f) + (b * 0.1805f);
float y = (r * 0.2126f) + (g * 0.7152f) + (b * 0.0722f);
float z = (r * 0.0193f) + (g * 0.1192f) + (b * 0.9505f);
return (x, y, z);
}
internal static (float r, float g, float b) CalculateRGBFromXYZ(float x, float y, float z)
{
const float INVERSE_EXPONENT = 1.0f / 2.4f;
x /= 100.0f;
y /= 100.0f;
z /= 100.0f;
float r = (x * 3.2406f) + (y * -1.5372f) + (z * -0.4986f);
float g = (x * -0.9689f) + (y * 1.8758f) + (z * 0.0415f);
float b = (x * 0.0557f) + (y * -0.2040f) + (z * 1.0570f);
r = ((r > 0.0031308f) ? ((1.055f * (MathF.Pow(r, INVERSE_EXPONENT))) - 0.055f) : (12.92f * r));
g = ((g > 0.0031308f) ? ((1.055f * (MathF.Pow(g, INVERSE_EXPONENT))) - 0.055f) : (12.92f * g));
b = ((b > 0.0031308f) ? ((1.055f * (MathF.Pow(b, INVERSE_EXPONENT))) - 0.055f) : (12.92f * b));
return (r, g, b);
}
#endregion
}

View File

@ -1,16 +1,15 @@
// ReSharper disable UnusedMember.Global
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Represents a generic color-correction.
/// </summary>
public interface IColorCorrection
{
/// <summary>
/// Represents a generic color-correction.
/// Applies the <see cref="IColorCorrection"/> to the specified <see cref="Color"/>.
/// </summary>
public interface IColorCorrection
{
/// <summary>
/// Applies the <see cref="IColorCorrection"/> to the given <see cref="Color"/>.
/// </summary>
/// <param name="color">The <see cref="Color"/> to correct.</param>
Color ApplyTo(Color color);
}
}
/// <param name="color">The <see cref="Color"/> to correct.</param>
void ApplyTo(ref Color color);
}

View File

@ -0,0 +1,8 @@
#if NET8_0
// ReSharper disable once CheckNamespace
namespace RGB.NET.Core.Compatibility.Net8;
public sealed class Lock;
#endif

View File

@ -2,64 +2,68 @@
using System.Collections.ObjectModel;
using System.Linq;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc cref="AbstractBindable" />
/// <inheritdoc cref="IDecoratable{T}" />
public abstract class AbstractDecoratable<T> : AbstractBindable, IDecoratable<T>
where T : IDecorator
{
/// <inheritdoc cref="AbstractBindable" />
/// <inheritdoc cref="IDecoratable{T}" />
public abstract class AbstractDecoratable<T> : AbstractBindable, IDecoratable<T>
where T : IDecorator
#region Properties & Fields
private readonly List<T> _decorators = [];
/// <inheritdoc />
public IReadOnlyList<T> Decorators { get; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractDecoratable{T}"/> class.
/// </summary>
protected AbstractDecoratable()
{
#region Properties & Fields
private readonly List<T> _decorators = new List<T>();
/// <inheritdoc />
public IReadOnlyCollection<T> Decorators
{
get
{
lock (_decorators)
return new ReadOnlyCollection<T>(_decorators);
}
}
#endregion
#region Methods
/// <inheritdoc />
public void AddDecorator(T decorator)
{
lock (Decorators)
{
_decorators.Add(decorator);
_decorators.Sort((d1, d2) => d1.Order.CompareTo(d2.Order));
}
decorator.OnAttached(this);
}
/// <inheritdoc />
public void RemoveDecorator(T decorator)
{
lock (Decorators)
_decorators.Remove(decorator);
decorator.OnDetached(this);
}
/// <inheritdoc />
public void RemoveAllDecorators()
{
IEnumerable<T> decorators;
lock (Decorators)
decorators = Decorators.ToList();
foreach (T decorator in decorators)
RemoveDecorator(decorator);
}
#endregion
Decorators = new ReadOnlyCollection<T>(_decorators);
}
}
#endregion
#region Methods
/// <inheritdoc />
public void AddDecorator(T decorator)
{
lock (Decorators)
{
_decorators.Add(decorator);
_decorators.Sort((d1, d2) => d1.Order.CompareTo(d2.Order));
}
decorator.OnAttached(this);
}
/// <inheritdoc />
public void RemoveDecorator(T decorator)
{
lock (Decorators)
_decorators.Remove(decorator);
decorator.OnDetached(this);
}
/// <inheritdoc />
public void RemoveAllDecorators()
{
IEnumerable<T> decorators;
lock (Decorators)
decorators = Decorators.ToList();
foreach (T decorator in decorators)
RemoveDecorator(decorator);
}
#endregion
}

View File

@ -2,61 +2,60 @@
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc cref="AbstractBindable" />
/// <inheritdoc cref="IDecorator" />
public abstract class AbstractDecorator : AbstractBindable, IDecorator
{
/// <inheritdoc cref="AbstractBindable" />
/// <inheritdoc cref="IDecorator" />
public abstract class AbstractDecorator : AbstractBindable, IDecorator
#region Properties & Fields
private bool _isEnabled = true;
/// <inheritdoc />
public bool IsEnabled
{
#region Properties & Fields
private bool _isEnabled = true;
/// <inheritdoc />
public bool IsEnabled
{
get => _isEnabled;
set => SetProperty(ref _isEnabled, value);
}
private int _order;
/// <inheritdoc />
public int Order
{
get => _order;
set => SetProperty(ref _order, value);
}
/// <summary>
/// Gets a readonly-list of all <see cref="IDecoratable"/> this decorator is attached to.
/// </summary>
protected List<IDecoratable> DecoratedObjects { get; } = new List<IDecoratable>();
#endregion
#region Methods
/// <inheritdoc />
public virtual void OnAttached(IDecoratable decoratable) => DecoratedObjects.Add(decoratable);
/// <inheritdoc />
public virtual void OnDetached(IDecoratable decoratable) => DecoratedObjects.Remove(decoratable);
/// <summary>
/// Detaches the decorator from all <see cref="IDecoratable"/> it is currently attached to.
/// </summary>
protected virtual void Detach()
{
List<IDecoratable> decoratables = new List<IDecoratable>(DecoratedObjects);
foreach (IDecoratable decoratable in decoratables)
{
IEnumerable<Type> types = decoratable.GetType().GetInterfaces().Where(t => t.IsGenericType
&& (t.Name == typeof(IDecoratable<>).Name)
&& t.GenericTypeArguments[0].IsInstanceOfType(this));
foreach (Type decoratableType in types)
decoratableType.GetMethod(nameof(IDecoratable<IDecorator>.RemoveDecorator))?.Invoke(decoratable, new object[] { this });
}
}
#endregion
get => _isEnabled;
set => SetProperty(ref _isEnabled, value);
}
}
private int _order;
/// <inheritdoc />
public int Order
{
get => _order;
set => SetProperty(ref _order, value);
}
/// <summary>
/// Gets a readonly-list of all <see cref="IDecoratable"/> this decorator is attached to.
/// </summary>
protected List<IDecoratable> DecoratedObjects { get; } = [];
#endregion
#region Methods
/// <inheritdoc />
public virtual void OnAttached(IDecoratable decoratable) => DecoratedObjects.Add(decoratable);
/// <inheritdoc />
public virtual void OnDetached(IDecoratable decoratable) => DecoratedObjects.Remove(decoratable);
/// <summary>
/// Detaches the decorator from all <see cref="IDecoratable"/> it is currently attached to.
/// </summary>
protected virtual void Detach()
{
List<IDecoratable> decoratables = [..DecoratedObjects];
foreach (IDecoratable decoratable in decoratables)
{
IEnumerable<Type> types = decoratable.GetType().GetInterfaces().Where(t => t.IsGenericType
&& (t.Name == typeof(IDecoratable<>).Name)
&& t.GenericTypeArguments[0].IsInstanceOfType(this));
foreach (Type decoratableType in types)
decoratableType.GetMethod(nameof(IDecoratable<IDecorator>.RemoveDecorator))?.Invoke(decoratable, [this]);
}
}
#endregion
}

View File

@ -1,65 +1,71 @@
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents a basic decorator which is aware of the <see cref="E:RGB.NET.Core.RGBSurface.Updating" /> event.
/// </summary>
public abstract class AbstractUpdateAwareDecorator : AbstractDecorator
{
/// <inheritdoc />
#region Properties & Fields
/// <summary>
/// Represents a basic decorator which is aware of the <see cref="E:RGB.NET.Core.RGBSurface.Updating" /> event.
/// Gets the surface this decorator is attached to.
/// </summary>
public abstract class AbstractUpdateAwareDecorator : AbstractDecorator
protected RGBSurface Surface { get; }
/// <summary>
/// Gets or sets if the <see cref="AbstractUpdateAwareDecorator"/> should call <see cref="Update"/> even if the Decorator is disabled.
/// </summary>
protected bool UpdateIfDisabled { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractUpdateAwareDecorator"/> class.
/// </summary>
/// <param name="surface">The surface this decorator is attached to.</param>
/// <param name="updateIfDisabled">Bool indicating if the <see cref="AbstractUpdateAwareDecorator"/> should call <see cref="Update"/> even if the Decorator is disabled.</param>
protected AbstractUpdateAwareDecorator(RGBSurface surface, bool updateIfDisabled = false)
{
#region Properties & Fields
/// <summary>
/// Gets or sets if the <see cref="AbstractUpdateAwareDecorator"/> should call <see cref="Update"/> even if the Decorator is disabled.
/// </summary>
protected bool UpdateIfDisabled { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractUpdateAwareDecorator"/> class.
/// </summary>
/// <param name="updateIfDisabled">Bool indicating if the <see cref="AbstractUpdateAwareDecorator"/> should call <see cref="Update"/> even if the Decorator is disabled.</param>
protected AbstractUpdateAwareDecorator(bool updateIfDisabled = false)
{
this.UpdateIfDisabled = updateIfDisabled;
}
#endregion
#region Methods
/// <inheritdoc />
public override void OnAttached(IDecoratable decoratable)
{
if (DecoratedObjects.Count == 0)
RGBSurface.Instance.Updating += OnSurfaceUpdating;
base.OnAttached(decoratable);
}
/// <inheritdoc />
public override void OnDetached(IDecoratable decoratable)
{
base.OnDetached(decoratable);
if (DecoratedObjects.Count == 0)
RGBSurface.Instance.Updating -= OnSurfaceUpdating;
}
private void OnSurfaceUpdating(UpdatingEventArgs args)
{
if (IsEnabled || UpdateIfDisabled)
Update(args.DeltaTime);
}
/// <summary>
/// Updates this <see cref="AbstractUpdateAwareDecorator"/>.
/// </summary>
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
protected abstract void Update(double deltaTime);
#endregion
this.Surface = surface;
this.UpdateIfDisabled = updateIfDisabled;
}
}
#endregion
#region Methods
/// <inheritdoc />
public override void OnAttached(IDecoratable decoratable)
{
if (DecoratedObjects.Count == 0)
Surface.Updating += OnSurfaceUpdating;
base.OnAttached(decoratable);
}
/// <inheritdoc />
public override void OnDetached(IDecoratable decoratable)
{
base.OnDetached(decoratable);
if (DecoratedObjects.Count == 0)
Surface.Updating -= OnSurfaceUpdating;
}
private void OnSurfaceUpdating(UpdatingEventArgs args)
{
if (IsEnabled || UpdateIfDisabled)
Update(args.DeltaTime);
}
/// <summary>
/// Updates this <see cref="AbstractUpdateAwareDecorator"/>.
/// </summary>
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
protected abstract void Update(double deltaTime);
#endregion
}

View File

@ -1,17 +1,16 @@
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents a <see cref="T:RGB.NET.Core.IDecorator" /> decorating a <see cref="T:RGB.NET.Core.IBrush" />.
/// </summary>
public interface IBrushDecorator : IDecorator
{
/// <inheritdoc />
/// <summary>
/// Represents a <see cref="T:RGB.NET.Core.IDecorator" /> decorating a <see cref="T:RGB.NET.Core.IBrush" />.
/// Decorator-Method called by the <see cref="IBrush"/>.
/// </summary>
public interface IBrushDecorator : IDecorator
{
/// <summary>
/// Decorator-Method called by the <see cref="IBrush"/>.
/// </summary>
/// <param name="rectangle">The rectangle in which the <see cref="IBrush"/> should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the <see cref="Color"/> should be taken.</param>
/// <param name="color">The <see cref="Color"/> to be modified.</param>
Color ManipulateColor(Rectangle rectangle, BrushRenderTarget renderTarget, Color color);
}
}
/// <param name="rectangle">The rectangle in which the <see cref="IBrush"/> should be drawn.</param>
/// <param name="renderTarget">The target (key/point) from which the <see cref="Color"/> should be taken.</param>
/// <param name="color">The <see cref="Color"/> to be modified.</param>
void ManipulateColor(Rectangle rectangle, RenderTarget renderTarget, ref Color color);
}

View File

@ -1,42 +1,40 @@
using System.Collections.Generic;
using System.ComponentModel;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Represents a basic decoratable.
/// </summary>
public interface IDecoratable : INotifyPropertyChanged;
/// <inheritdoc />
/// <summary>
/// Represents a basic decoratable for a specific type of <see cref="T:RGB.NET.Core.IDecorator" />
/// </summary>
/// <typeparam name="T">The type of decorators this decoratable can be decorated with.</typeparam>
public interface IDecoratable<T> : IDecoratable
where T : IDecorator
{
/// <summary>
/// Represents a basic decoratable.
/// Gets a readonly-list of all <see cref="IDecorator"/> attached to this <see cref="IDecoratable{T}"/>.
/// </summary>
public interface IDecoratable : INotifyPropertyChanged
{ }
IReadOnlyList<T> Decorators { get; }
/// <inheritdoc />
/// <summary>
/// Represents a basic decoratable for a specific type of <see cref="T:RGB.NET.Core.IDecorator" />
/// Adds an <see cref="IDecorator"/> to the <see cref="IDecoratable"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IDecoratable<T> : IDecoratable
where T : IDecorator
{
/// <summary>
/// Gets a readonly-list of all <see cref="IDecorator"/> attached to this <see cref="IDecoratable{T}"/>.
/// </summary>
IReadOnlyCollection<T> Decorators { get; }
/// <param name="decorator">The <see cref="IDecorator"/> to be added.</param>
void AddDecorator(T decorator);
/// <summary>
/// Adds an <see cref="IDecorator"/> to the <see cref="IDecoratable"/>.
/// </summary>
/// <param name="decorator">The <see cref="IDecorator"/> to be added.</param>
void AddDecorator(T decorator);
/// <summary>
/// Removes an <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
/// </summary>
/// <param name="decorator">The <see cref="IDecorator"/> to be removed.</param>
void RemoveDecorator(T decorator);
/// <summary>
/// Removes an <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
/// </summary>
/// <param name="decorator">The <see cref="IDecorator"/> to be removed.</param>
void RemoveDecorator(T decorator);
/// <summary>
/// Removes all <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
/// </summary>
void RemoveAllDecorators();
}
}
/// <summary>
/// Removes all <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
/// </summary>
void RemoveAllDecorators();
}

View File

@ -1,39 +1,38 @@
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Represents a basic decorator.
/// </summary>
public interface IDecorator
{
#region Properties & Fields
/// <summary>
/// Represents a basic decorator.
/// Gets or sets if the <see cref="IDecorator"/> is enabled and will be used.
/// </summary>
public interface IDecorator
{
#region Properties & Fields
bool IsEnabled { get; set; }
/// <summary>
/// Gets or sets if the <see cref="IDecorator"/> is enabled and will be used.
/// </summary>
bool IsEnabled { get; set; }
/// <summary>
/// Gets or sets the order in which multiple decorators should be applied on the same object.
/// Higher orders are processed first.
/// </summary>
int Order { get; set; }
/// <summary>
/// Gets or sets the order in which multiple decorators should be applied on the same object.
/// Higher orders are processed first.
/// </summary>
int Order { get; set; }
#endregion
#endregion
#region Methods
#region Methods
/// <summary>
/// Attaches this <see cref="IDecorator"/> to the specified target.
/// </summary>
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be attached to.</param>
void OnAttached(IDecoratable decoratable);
/// <summary>
/// Attaches this <see cref="IDecorator"/> to the given target.
/// </summary>
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be attached to.</param>
void OnAttached(IDecoratable decoratable);
/// <summary>
/// Detaches this <see cref="IDecorator"/> from the specified target.
/// </summary>
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be detached from.</param>
void OnDetached(IDecoratable decoratable);
/// <summary>
/// Detaches this <see cref="IDecorator"/> from the given target.
/// </summary>
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be detached from.</param>
void OnDetached(IDecoratable decoratable);
#endregion
}
}
#endregion
}

View File

@ -1,9 +1,7 @@
namespace RGB.NET.Core
{
/// <inheritdoc />
/// <summary>
/// Represents a basic decorator decorating a <see cref="T:RGB.NET.Core.ILedGroup" />.
/// </summary>
public interface ILedGroupDecorator : IDecorator
{ }
}
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents a basic decorator decorating a <see cref="T:RGB.NET.Core.ILedGroup" />.
/// </summary>
public interface ILedGroupDecorator : IDecorator;

View File

@ -3,296 +3,246 @@
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using RGB.NET.Core.Layout;
using System.Runtime.CompilerServices;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc cref="AbstractBindable" />
/// <inheritdoc cref="IRGBDevice{TDeviceInfo}" />
/// <summary>
/// Represents a generic RGB-device.
/// </summary>
public abstract class AbstractRGBDevice<TDeviceInfo> : Placeable, IRGBDevice<TDeviceInfo>
where TDeviceInfo : class, IRGBDeviceInfo
{
/// <inheritdoc cref="AbstractBindable" />
/// <inheritdoc cref="IRGBDevice{TDeviceInfo}" />
/// <summary>
/// Represents a generic RGB-device.
/// </summary>
public abstract class AbstractRGBDevice<TDeviceInfo> : AbstractBindable, IRGBDevice<TDeviceInfo>
where TDeviceInfo : class, IRGBDeviceInfo
private RGBSurface? _surface;
#region Properties & Fields
RGBSurface? IRGBDevice.Surface
{
#region Properties & Fields
/// <inheritdoc />
public abstract TDeviceInfo DeviceInfo { get; }
/// <inheritdoc />
IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo;
private Point _location = new Point(0, 0);
/// <inheritdoc />
public Point Location
get => _surface;
set
{
get => _location;
set
if (SetProperty(ref _surface, value))
{
if (SetProperty(ref _location, value))
UpdateActualData();
if (value == null) OnDetached();
else OnAttached();
}
}
private Size _size = Size.Invalid;
/// <inheritdoc />
public Size Size
{
get => _size;
protected set
{
if (SetProperty(ref _size, value))
UpdateActualData();
}
}
private Size _actualSize;
/// <inheritdoc />
public Size ActualSize
{
get => _actualSize;
private set => SetProperty(ref _actualSize, value);
}
private Rectangle _deviceRectangle;
/// <inheritdoc />
public Rectangle DeviceRectangle
{
get => _deviceRectangle;
private set => SetProperty(ref _deviceRectangle, value);
}
private Scale _scale = new Scale(1);
/// <inheritdoc />
public Scale Scale
{
get => _scale;
set
{
if (SetProperty(ref _scale, value))
UpdateActualData();
}
}
private Rotation _rotation = new Rotation(0);
/// <inheritdoc />
public Rotation Rotation
{
get => _rotation;
set
{
if (SetProperty(ref _rotation, value))
UpdateActualData();
}
}
/// <summary>
/// Gets or sets if the device needs to be flushed on every update.
/// </summary>
protected bool RequiresFlush { get; set; } = false;
/// <inheritdoc />
public DeviceUpdateMode UpdateMode { get; set; } = DeviceUpdateMode.Sync;
/// <summary>
/// Gets a dictionary containing all <see cref="Led"/> of the <see cref="IRGBDevice"/>.
/// </summary>
protected Dictionary<LedId, Led> LedMapping { get; } = new Dictionary<LedId, Led>();
/// <summary>
/// Gets a dictionary containing all <see cref="IRGBDeviceSpecialPart"/> associated with this <see cref="IRGBDevice"/>.
/// </summary>
protected Dictionary<Type, IRGBDeviceSpecialPart> SpecialDeviceParts { get; } = new Dictionary<Type, IRGBDeviceSpecialPart>();
#region Indexer
/// <inheritdoc />
Led IRGBDevice.this[LedId ledId] => LedMapping.TryGetValue(ledId, out Led led) ? led : null;
/// <inheritdoc />
Led IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.LedRectangle.Contains(location));
/// <inheritdoc />
IEnumerable<Led> IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage]
=> LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.LedRectangle) >= minOverlayPercentage);
#endregion
#endregion
#region Methods
private void UpdateActualData()
{
ActualSize = Size * Scale;
DeviceRectangle = new Rectangle(Location, new Rectangle(new Rectangle(Location, ActualSize).Rotate(Rotation)).Size);
}
/// <inheritdoc />
public virtual void Update(bool flushLeds = false)
{
// Device-specific updates
DeviceUpdate();
// Send LEDs to SDK
List<Led> ledsToUpdate = GetLedsToUpdate(flushLeds)?.ToList() ?? new List<Led>();
foreach (Led ledToUpdate in ledsToUpdate)
ledToUpdate.Update();
if (UpdateMode.HasFlag(DeviceUpdateMode.Sync))
UpdateLeds(ledsToUpdate);
}
protected virtual IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => ((RequiresFlush || flushLeds) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty));
/// <inheritdoc />
public virtual void SyncBack()
{ }
/// <inheritdoc />
public virtual void Dispose()
{
try
{
SpecialDeviceParts.Clear();
LedMapping.Clear();
}
catch { /* this really shouldn't happen */ }
}
/// <summary>
/// Performs device specific updates.
/// </summary>
protected virtual void DeviceUpdate()
{ }
/// <summary>
/// Sends all the updated <see cref="Led"/> to the device.
/// </summary>
protected abstract void UpdateLeds(IEnumerable<Led> ledsToUpdate);
/// <summary>
/// Initializes the <see cref="Led"/> with the specified id.
/// </summary>
/// <param name="ledId">The <see cref="LedId"/> to initialize.</param>
/// <param name="ledRectangle">The <see cref="Rectangle"/> representing the position of the <see cref="Led"/> to initialize.</param>
/// <returns></returns>
[Obsolete("Use InitializeLed(LedId ledId, Point location, Size size) instead.")]
protected virtual Led InitializeLed(LedId ledId, Rectangle rectangle) => InitializeLed(ledId, rectangle.Location, rectangle.Size);
/// <summary>
/// Initializes the <see cref="Led"/> with the specified id.
/// </summary>
/// <param name="ledId">The <see cref="LedId"/> to initialize.</param>
/// <param name="location">The location of the <see cref="Led"/> to initialize.</param>
/// <param name="size">The size of the <see cref="Led"/> to initialize.</param>
/// <returns>The initialized led.</returns>
protected virtual Led InitializeLed(LedId ledId, Point location, Size size)
{
if ((ledId == LedId.Invalid) || LedMapping.ContainsKey(ledId)) return null;
Led led = new Led(this, ledId, location, size, CreateLedCustomData(ledId));
LedMapping.Add(ledId, led);
return led;
}
/// <summary>
/// Applies the give <see cref="Color"/> to the <see cref="Led"/> ignoring internal workflows regarding locks and update-requests.
/// This should be only used for syncbacks!
/// </summary>
/// <param name="led">The <see cref="Led"/> the <see cref="Color"/> should be aplied to.</param>
/// <param name="color">The <see cref="Color"/> to apply.</param>
protected virtual void SetLedColorWithoutRequest(Led led, Color color)
{
if (led == null) return;
led.InternalColor = color;
}
/// <summary>
/// Applies the given layout.
/// </summary>
/// <param name="layoutPath">The file containing the layout.</param>
/// <param name="imageLayout">The name of the layout used to get the images of the leds.</param>
/// <param name="createMissingLeds">If set to true a new led is initialized for every id in the layout if it doesn't already exist.</param>
protected virtual DeviceLayout ApplyLayoutFromFile(string layoutPath, string imageLayout, bool createMissingLeds = false)
{
DeviceLayout layout = DeviceLayout.Load(layoutPath);
if (layout != null)
{
string imageBasePath = string.IsNullOrWhiteSpace(layout.ImageBasePath) ? null : PathHelper.GetAbsolutePath(this, layout.ImageBasePath);
if ((imageBasePath != null) && !string.IsNullOrWhiteSpace(layout.DeviceImage) && (DeviceInfo != null))
DeviceInfo.Image = new Uri(Path.Combine(imageBasePath, layout.DeviceImage), UriKind.Absolute);
LedImageLayout ledImageLayout = layout.LedImageLayouts.FirstOrDefault(x => string.Equals(x.Layout, imageLayout, StringComparison.OrdinalIgnoreCase));
Size = new Size(layout.Width, layout.Height);
if (layout.Leds != null)
foreach (LedLayout layoutLed in layout.Leds)
{
if (Enum.TryParse(layoutLed.Id, true, out LedId ledId))
{
if (!LedMapping.TryGetValue(ledId, out Led led) && createMissingLeds)
led = InitializeLed(ledId, new Point(), new Size());
if (led != null)
{
led.Location = new Point(layoutLed.X, layoutLed.Y);
led.Size = new Size(layoutLed.Width, layoutLed.Height);
led.Shape = layoutLed.Shape;
led.ShapeData = layoutLed.ShapeData;
LedImage image = ledImageLayout?.LedImages.FirstOrDefault(x => x.Id == layoutLed.Id);
if ((imageBasePath != null) && !string.IsNullOrEmpty(image?.Image))
led.Image = new Uri(Path.Combine(imageBasePath, image.Image), UriKind.Absolute);
}
}
}
}
return layout;
}
/// <summary>
/// Creates provider-specific data associated with this <see cref="LedId"/>.
/// </summary>
/// <param name="ledId">The <see cref="LedId"/>.</param>
protected virtual object CreateLedCustomData(LedId ledId) => null;
/// <inheritdoc />
public void AddSpecialDevicePart<T>(T specialDevicePart)
where T : class, IRGBDeviceSpecialPart
=> SpecialDeviceParts[typeof(T)] = specialDevicePart;
/// <inheritdoc />
public T GetSpecialDevicePart<T>()
where T : class, IRGBDeviceSpecialPart
=> SpecialDeviceParts.TryGetValue(typeof(T), out IRGBDeviceSpecialPart devicePart) ? (T)devicePart : default;
#region Enumerator
/// <inheritdoc />
/// <summary>
/// Returns an enumerator that iterates over all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.
/// </summary>
/// <returns>An enumerator for all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.</returns>
public IEnumerator<Led> GetEnumerator() => LedMapping.Values.GetEnumerator();
/// <inheritdoc />
/// <summary>
/// Returns an enumerator that iterates over all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.
/// </summary>
/// <returns>An enumerator for all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.</returns>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
#endregion
}
}
/// <inheritdoc />
public TDeviceInfo DeviceInfo { get; }
/// <inheritdoc />
IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo;
/// <inheritdoc />
public IList<IColorCorrection> ColorCorrections { get; } = [];
/// <summary>
/// Gets or sets if the device needs to be flushed on every update.
/// </summary>
protected bool RequiresFlush { get; set; } = false;
/// <summary>
/// Gets a dictionary containing all <see cref="Led"/> of the <see cref="IRGBDevice"/>.
/// </summary>
protected Dictionary<LedId, Led> LedMapping { get; } = [];
/// <summary>
/// Gets the update queue used to update this device.
/// </summary>
protected IUpdateQueue UpdateQueue { get; }
#region Indexer
/// <inheritdoc />
Led? IRGBDevice.this[LedId ledId] => LedMapping.GetValueOrDefault(ledId);
/// <inheritdoc />
Led? IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.Boundary.Contains(location));
/// <inheritdoc />
IEnumerable<Led> IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage]
=> LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.Boundary) >= minOverlayPercentage);
#endregion
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractRGBDevice{T}"/> class.
/// </summary>
/// <param name="deviceInfo">The device info of this device.</param>
/// <param name="updateQueue">The queue used to update this device.</param>
protected AbstractRGBDevice(TDeviceInfo deviceInfo, IUpdateQueue updateQueue)
{
this.DeviceInfo = deviceInfo;
this.UpdateQueue = updateQueue;
UpdateQueue.AddReferencingObject(this);
}
#endregion
#region Methods
/// <inheritdoc />
public virtual void Update(bool flushLeds = false)
{
// Device-specific updates
DeviceUpdate();
// Send LEDs to SDK
UpdateLeds(GetLedsToUpdate(flushLeds));
}
/// <summary>
/// Gets an enumerable of LEDs that are changed and requires an update.
/// </summary>
/// <param name="flushLeds">Forces all LEDs to be treated as dirty.</param>
/// <returns>The collection LEDs to update.</returns>
protected virtual IEnumerable<Led> GetLedsToUpdate(bool flushLeds) => ((RequiresFlush || flushLeds || UpdateQueue.RequiresFlush) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty)).Where(led => led.RequestedColor?.A > 0);
/// <summary>
/// Gets an enumerable of a custom data and color tuple for the specified leds.
/// </summary>
/// <remarks>
/// Applies all <see cref="ColorCorrections"/>.
/// if no <see cref="Led.CustomData"/> ist specified the <see cref="Led.Id"/> is used.
/// </remarks>
/// <param name="led">The of led to convert.</param>
/// <returns>The enumerable of custom data and color tuples for the specified leds.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected (object key, Color color) GetUpdateData(Led led)
{
Color color = led.Color;
object key = led.CustomData ?? led.Id;
// ReSharper disable once ForCanBeConvertedToForeach - This causes an allocation that's not really needed here
for (int i = 0; i < ColorCorrections.Count; i++)
ColorCorrections[i].ApplyTo(ref color);
return (key, color);
}
/// <summary>
/// Sends all the updated <see cref="Led"/> to the device.
/// </summary>
protected virtual void UpdateLeds(IEnumerable<Led> ledsToUpdate)
{
(object key, Color color)[] buffer = ArrayPool<(object, Color)>.Shared.Rent(LedMapping.Count);
int counter = 0;
foreach (Led led in ledsToUpdate)
{
led.Update();
buffer[counter] = GetUpdateData(led);
++counter;
}
UpdateQueue.SetData(new ReadOnlySpan<(object, Color)>(buffer)[..counter]);
ArrayPool<(object, Color)>.Shared.Return(buffer);
}
/// <inheritdoc />
public virtual void Dispose()
{
try
{
UpdateQueue.RemoveReferencingObject(this);
if (!UpdateQueue.HasActiveReferences())
UpdateQueue.Dispose();
}
catch { /* :( */ }
try { LedMapping.Clear(); } catch { /* this really shouldn't happen */ }
IdGenerator.ResetCounter(GetType().Assembly);
GC.SuppressFinalize(this);
}
/// <summary>
/// Performs device specific updates.
/// </summary>
protected virtual void DeviceUpdate()
{ }
/// <inheritdoc />
public virtual Led? AddLed(LedId ledId, Point location, Size size, object? customData = null)
{
if ((ledId == LedId.Invalid) || LedMapping.ContainsKey(ledId)) return null;
Led led = new(this, ledId, location, size, customData ?? GetLedCustomData(ledId));
LedMapping.Add(ledId, led);
return led;
}
/// <inheritdoc />
public virtual Led? RemoveLed(LedId ledId)
{
if (ledId == LedId.Invalid) return null;
if (!LedMapping.TryGetValue(ledId, out Led? led)) return null;
LedMapping.Remove(ledId);
return led;
}
/// <summary>
/// Gets the custom data associated with the specified LED.
/// </summary>
/// <param name="ledId">The id of the led.</param>
/// <returns>The custom data for the specified LED.</returns>
protected virtual object? GetLedCustomData(LedId ledId) => null;
/// <summary>
/// Called when the device is attached to a surface.
/// </summary>
/// <remarks>
/// When overriden base should be called to validate boundries.
/// </remarks>
protected virtual void OnAttached()
{
if (Location == Point.Invalid) Location = new Point(0, 0);
if (Size == Size.Invalid)
{
Rectangle ledRectangle = new(this.Select(x => x.Boundary));
Size = ledRectangle.Size + new Size(ledRectangle.Location.X, ledRectangle.Location.Y);
}
}
/// <summary>
/// Called when the device is detached from a surface.
/// </summary>
protected virtual void OnDetached() { }
#region Enumerator
/// <inheritdoc />
/// <summary>
/// Returns an enumerator that iterates over all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.
/// </summary>
/// <returns>An enumerator for all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.</returns>
public IEnumerator<Led> GetEnumerator() => LedMapping.Values.GetEnumerator();
/// <inheritdoc />
/// <summary>
/// Returns an enumerator that iterates over all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.
/// </summary>
/// <returns>An enumerator for all <see cref="T:RGB.NET.Core.Led" /> of the <see cref="T:RGB.NET.Core.IRGBDevice" />.</returns>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion
#endregion
}

View File

@ -0,0 +1,282 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace RGB.NET.Core;
/// <summary>
/// Represents the abstract base implementation for a <see cref="IRGBDeviceProvider"/>.
/// </summary>
public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
{
#region Properties & Fields
private bool _isDisposed = false;
private readonly double _defaultUpdateRateHardLimit;
/// <inheritdoc />
public bool IsInitialized { get; protected set; }
/// <inheritdoc />
public bool ThrowsExceptions { get; protected set; }
/// <summary>
/// The list of devices managed by this device-provider.
/// </summary>
protected List<IRGBDevice> InternalDevices { get; } = [];
/// <inheritdoc />
public virtual IReadOnlyList<IRGBDevice> Devices => new ReadOnlyCollection<IRGBDevice>(InternalDevices);
/// <summary>
/// Gets the dictionary containing the registered update triggers.
/// Normally <see cref="UpdateTriggers"/> should be used to access them.
/// </summary>
protected Dictionary<int, IDeviceUpdateTrigger> UpdateTriggerMapping { get; } = [];
/// <inheritdoc />
public IReadOnlyList<(int id, IDeviceUpdateTrigger trigger)> UpdateTriggers => new ReadOnlyCollection<(int id, IDeviceUpdateTrigger trigger)>(UpdateTriggerMapping.Select(x => (x.Key, x.Value)).ToList());
#endregion
#region Events
/// <inheritdoc />
public event EventHandler<ExceptionEventArgs>? Exception;
/// <inheritdoc />
public event EventHandler<DevicesChangedEventArgs>? DevicesChanged;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractRGBDeviceProvider" /> class.
/// </summary>
/// <param name="defaultUpdateRateHardLimit">The update rate hard limit all update triggers for this device provider are initialized with.</param>
protected AbstractRGBDeviceProvider(double defaultUpdateRateHardLimit = 0)
{
this._defaultUpdateRateHardLimit = defaultUpdateRateHardLimit;
}
~AbstractRGBDeviceProvider() => Dispose(false);
#endregion
#region Methods
/// <inheritdoc />
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
ThrowsExceptions = throwExceptions;
try
{
Reset();
InitializeSDK();
foreach (IRGBDevice device in GetLoadedDevices(loadFilter))
AddDevice(device);
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
updateTrigger.Start();
IsInitialized = true;
}
catch (DeviceProviderException)
{
Reset();
throw;
}
catch (Exception ex)
{
Reset();
Throw(ex, true);
return false;
}
return true;
}
/// <summary>
/// Loads devices and returns a filtered list of them.
/// </summary>
/// <remarks>
/// The underlying loading of the devices happens in <see cref="LoadDevices"/>.
/// </remarks>
/// <param name="loadFilter"><see cref="RGBDeviceType"/>-flags to filter the device with.</param>
/// <returns>The filtered collection of loaded devices.</returns>
protected virtual IEnumerable<IRGBDevice> GetLoadedDevices(RGBDeviceType loadFilter)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
List<IRGBDevice> devices = [];
foreach (IRGBDevice device in LoadDevices())
{
try
{
if (loadFilter.HasFlag(device.DeviceInfo.DeviceType))
devices.Add(device);
else
device.Dispose();
}
catch (Exception ex)
{
Throw(ex);
}
}
return devices;
}
/// <summary>
/// Initializes the underlying SDK.
/// </summary>
protected abstract void InitializeSDK();
/// <summary>
/// Loads all devices this device provider is capable of loading.
/// </summary>
/// <remarks>
/// Filtering happens in <see cref="GetLoadedDevices"/>.
/// </remarks>
/// <returns>A collection of loaded devices.</returns>
protected abstract IEnumerable<IRGBDevice> LoadDevices();
/// <summary>
/// Gets the <see cref="IDeviceUpdateTrigger"/> mapped to the specified id or a new one if the id wasn't requested before.
/// </summary>
/// <remarks>
/// The creation of the update trigger happens in <see cref="CreateUpdateTrigger"/>.
/// </remarks>
/// <param name="id">The id of the update trigger.</param>
/// <param name="updateRateHardLimit">The update rate hard limit to be set in the update trigger.</param>
/// <returns>The update trigger mapped to the specified id.</returns>
protected virtual IDeviceUpdateTrigger GetUpdateTrigger(int id = -1, double? updateRateHardLimit = null)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
if (!UpdateTriggerMapping.TryGetValue(id, out IDeviceUpdateTrigger? updaeTrigger))
UpdateTriggerMapping[id] = (updaeTrigger = CreateUpdateTrigger(id, updateRateHardLimit ?? _defaultUpdateRateHardLimit));
return updaeTrigger;
}
/// <summary>
/// Creates a update trigger with the specified id and the specified update rate hard limit.
/// </summary>
/// <param name="id">The id of the update trigger.</param>
/// <param name="updateRateHardLimit">The update rate hard limit tobe set in the update trigger.</param>
/// <returns>The newly created update trigger.</returns>
protected virtual IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit);
/// <summary>
/// Resets the device provider and disposes all devices and update triggers.
/// </summary>
protected virtual void Reset()
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
updateTrigger.Dispose();
foreach (IRGBDevice device in Devices)
device.Dispose();
List<IRGBDevice> devices = [..InternalDevices];
foreach (IRGBDevice device in devices)
RemoveDevice(device);
UpdateTriggerMapping.Clear();
IsInitialized = false;
}
/// <summary>
/// Adds the provided device to the list of managed devices.
/// </summary>
/// <param name="device">The device to add.</param>
/// <returns><c>true</c> if the device was added successfully; otherwise <c>false</c>.</returns>
protected virtual bool AddDevice(IRGBDevice device)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
if (InternalDevices.Contains(device)) return false;
InternalDevices.Add(device);
try { OnDevicesChanged(DevicesChangedEventArgs.CreateDevicesAddedArgs(device)); } catch { /* we don't want to throw due to bad event handlers */ }
return true;
}
/// <summary>
/// Removes the provided device from the list of managed devices.
/// </summary>
/// <param name="device">The device to remove.</param>
/// <returns><c>true</c> if the device was removed successfully; otherwise <c>false</c>.</returns>
protected virtual bool RemoveDevice(IRGBDevice device)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
if (!InternalDevices.Remove(device)) return false;
try { OnDevicesChanged(DevicesChangedEventArgs.CreateDevicesRemovedArgs(device)); } catch { /* we don't want to throw due to bad event handlers */ }
return true;
}
/// <summary>
/// Triggers the <see cref="Exception"/>-event and throws the specified exception if <see cref="ThrowsExceptions"/> is true and it is not overriden in the event.
/// </summary>
/// <param name="ex">The exception to throw.</param>
/// <param name="isCritical">Indicates if the exception is critical for device provider to work correctly.</param>
public virtual void Throw(Exception ex, bool isCritical = false)
{
ExceptionEventArgs args = new(ex, isCritical, ThrowsExceptions);
try { OnException(args); } catch { /* we don't want to throw due to bad event handlers */ }
if (args.Throw)
throw new DeviceProviderException(ex, isCritical);
}
/// <summary>
/// Throws the <see cref="Exception"/>.event.
/// </summary>
/// <param name="args">The parameters passed to the event.</param>
protected virtual void OnException(ExceptionEventArgs args) => Exception?.Invoke(this, args);
/// <summary>
/// Throws the <see cref="DevicesChanged"/>-event.
/// </summary>
/// <param name="args">The parameters passed to the event.</param>
protected virtual void OnDevicesChanged(DevicesChangedEventArgs args) => DevicesChanged?.Invoke(this, args);
/// <inheritdoc />
public void Dispose()
{
if (_isDisposed) return;
try
{
Dispose(true);
}
catch { /* don't throw in dispose! */ }
GC.SuppressFinalize(this);
_isDisposed = true;
}
/// <summary>
/// Disposes the object and frees all resources.
/// </summary>
/// <param name="disposing"><c>true</c> if explicitely called through the Dispose-Method, <c>false</c> if called by the destructor.</param>
protected virtual void Dispose(bool disposing) => Reset();
#endregion
}

View File

@ -1,32 +0,0 @@
using System;
namespace RGB.NET.Core
{
/// <summary>
/// Contains a list of different device device update modes.
/// </summary>
[Flags]
public enum DeviceUpdateMode
{
/// <summary>
/// Represents nothing.
/// </summary>
None = 0,
/// <summary>
/// Represents a mode which updates the leds of the device.
/// </summary>
Sync = 1 << 0,
/// <summary>
/// Represents a mode which reads the color of the leds of the device.
/// This isn't supported by all devices!
/// </summary>
SyncBack = 1 << 1,
/// <summary>
/// Represents all update modes.
/// </summary>
NoUpdate = 1 << 0xFF
}
}

View File

@ -1,129 +1,98 @@
using System;
using System.Collections.Generic;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc cref="IEnumerable{Led}" />
/// <inheritdoc cref="IBindable" />
/// <inheritdoc cref="IDisposable" />
/// <summary>
/// Represents a generic RGB-device.
/// </summary>
public interface IRGBDevice : IEnumerable<Led>, IPlaceable, IBindable, IDisposable
{
/// <inheritdoc cref="IEnumerable{T}" />
/// <inheritdoc cref="IBindable" />
/// <inheritdoc cref="IDisposable" />
#region Properties
/// <summary>
/// Represents a generic RGB-device.
/// Gets the surface this device is attached to.
/// </summary>
public interface IRGBDevice : IEnumerable<Led>, IBindable, IDisposable
{
#region Properties
RGBSurface? Surface { get; internal set; }
/// <summary>
/// Gets generic information about the <see cref="IRGBDevice"/>.
/// </summary>
IRGBDeviceInfo DeviceInfo { get; }
/// <summary>
/// Gets or sets the location of the <see cref="IRGBDevice"/>.
/// </summary>
Point Location { get; set; }
/// <summary>
/// Gets the <see cref="Size"/> of the <see cref="IRGBDevice"/>.
/// </summary>
Size Size { get; }
/// <summary>
/// Gets the actual <see cref="Size"/> of the <see cref="IRGBDevice"/>.
/// This includes the <see cref="Scale"/>.
/// </summary>
Size ActualSize { get; }
/// <summary>
/// Gets a <see cref="Rectangle"/> representing the logical location of the <see cref="DeviceRectangle"/> relative to the <see cref="RGBSurface"/>.
/// </summary>
Rectangle DeviceRectangle { get; }
/// <summary>
/// Gets or sets the scale of the <see cref="IRGBDevice"/>.
/// </summary>
Scale Scale { get; set; }
/// <summary>
/// Gets or sets the rotation of the <see cref="IRGBDevice"/>.
/// </summary>
Rotation Rotation { get; set; }
/// <summary>
/// Gets or sets the <see cref="DeviceUpdateMode"/> of the <see cref="IRGBDevice"/>.
/// </summary>
DeviceUpdateMode UpdateMode { get; set; }
#endregion
#region Indexer
/// <summary>
/// Gets the <see cref="Led"/> with the specified <see cref="LedId"/>.
/// </summary>
/// <param name="ledId">The <see cref="LedId"/> of the <see cref="Led"/> to get.</param>
/// <returns>The <see cref="Led"/> with the specified <see cref="LedId"/> or null if no <see cref="Led"/> is found.</returns>
Led this[LedId ledId] { get; }
/// <summary>
/// Gets the <see cref="Led" /> at the given physical location.
/// </summary>
/// <param name="location">The <see cref="Point"/> to get the location from.</param>
/// <returns>The <see cref="Led"/> at the given <see cref="Point"/> or null if no location is found.</returns>
Led this[Point location] { get; }
/// <summary>
/// Gets a list of <see cref="Led" /> inside the given <see cref="Rectangle"/>.
/// </summary>
/// <param name="referenceRect">The <see cref="Rectangle"/> to check.</param>
/// <param name="minOverlayPercentage">The minimal percentage overlay a <see cref="Led"/> must have with the <see cref="Rectangle" /> to be taken into the list.</param>
/// <returns></returns>
IEnumerable<Led> this[Rectangle referenceRect, double minOverlayPercentage = 0.5] { get; }
#endregion
#region Methods
/// <summary>
/// Perform an update for all dirty <see cref="Led"/>, or all <see cref="Led"/> if flushLeds is set to true.
/// </summary>
/// <param name="flushLeds">Specifies whether all <see cref="Led"/> (including clean ones) should be updated.</param>
void Update(bool flushLeds = false);
/// <summary>
/// Synchronizes the internal state of the device to the real (physical) state.
/// This isn't supported by all devices! Check <see cref="IRGBDeviceInfo.SupportsSyncBack"/> to see if it's supported or not.
/// </summary>
void SyncBack();
/// <summary>
/// Adds the given <see cref="IRGBDeviceSpecialPart"/> to the device.
/// This will override existing <see cref="IRGBDeviceSpecialPart"/> of the same type.
/// </summary>
/// <param name="specialDevicePart">The <see cref="IRGBDeviceSpecialPart"/> to add.</param>
/// <typeparam name="T">The generic typeof of the <see cref="IRGBDeviceSpecialPart"/> to add.</typeparam>
void AddSpecialDevicePart<T>(T specialDevicePart) where T : class, IRGBDeviceSpecialPart;
/// <summary>
/// Gets the requested <see cref="IRGBDeviceSpecialPart"/> if available on this <see cref="IRGBDevice"/>.
/// </summary>
/// <typeparam name="T">The generic type of the requested <see cref="IRGBDeviceSpecialPart"/>.</typeparam>
/// <returns>The requested <see cref="IRGBDeviceSpecialPart"/> or null if not available in this <see cref="IRGBDevice"/>.</returns>
T GetSpecialDevicePart<T>() where T : class, IRGBDeviceSpecialPart;
#endregion
}
/// <inheritdoc />
/// <summary>
/// Represents a generic RGB-device with an known device-info type.
/// Gets generic information about the <see cref="IRGBDevice"/>.
/// </summary>
public interface IRGBDevice<out TDeviceInfo> : IRGBDevice
where TDeviceInfo : IRGBDeviceInfo
{
/// <summary>
/// Gets generic information about the <see cref="IRGBDevice"/>.
/// </summary>
new TDeviceInfo DeviceInfo { get; }
}
IRGBDeviceInfo DeviceInfo { get; }
/// <summary>
/// Gets a list of color corrections applied to this device.
/// </summary>
IList<IColorCorrection> ColorCorrections { get; }
#endregion
#region Indexer
/// <summary>
/// Gets the <see cref="Led"/> with the specified <see cref="LedId"/>.
/// </summary>
/// <param name="ledId">The <see cref="LedId"/> of the <see cref="Led"/> to get.</param>
/// <returns>The <see cref="Led"/> with the specified <see cref="LedId"/> or null if no <see cref="Led"/> is found.</returns>
Led? this[LedId ledId] { get; }
/// <summary>
/// Gets the <see cref="Led" /> at the specified physical location.
/// </summary>
/// <param name="location">The <see cref="Point"/> to get the location from.</param>
/// <returns>The <see cref="Led"/> at the specified <see cref="Point"/> or null if no location is found.</returns>
Led? this[Point location] { get; }
/// <summary>
/// Gets a list of <see cref="Led" /> inside the specified <see cref="Rectangle"/>.
/// </summary>
/// <param name="referenceRect">The <see cref="Rectangle"/> to check.</param>
/// <param name="minOverlayPercentage">The minimal percentage overlay a <see cref="Led"/> must have with the <see cref="Rectangle" /> to be taken into the list.</param>
/// <returns>A enumerable of leds inside the specified rectangle.</returns>
IEnumerable<Led> this[Rectangle referenceRect, double minOverlayPercentage = 0.5] { get; }
#endregion
#region Methods
/// <summary>
/// Perform an update for all dirty <see cref="Led"/>, or all <see cref="Led"/> if flushLeds is set to true.
/// </summary>
/// <param name="flushLeds">Specifies whether all <see cref="Led"/> (including clean ones) should be updated.</param>
void Update(bool flushLeds = false);
/// <summary>
/// Adds a led to the device.
/// </summary>
/// <param name="ledId">The id of the led.</param>
/// <param name="location">The location of the led on the device.</param>
/// <param name="size">The size of the led.</param>
/// <param name="customData">Custom data saved on the led.</param>
/// <returns>The newly added led or <c>null</c> if a led with this id is already added.</returns>
Led? AddLed(LedId ledId, Point location, Size size, object? customData = null);
/// <summary>
/// Removes the led with the specified id from the device.
/// </summary>
/// <param name="ledId">The id of the led to remove.</param>
/// <returns>The removed led or <c>null</c> if there was no led with the specified id.</returns>
Led? RemoveLed(LedId ledId);
#endregion
}
/// <inheritdoc />
/// <summary>
/// Represents a generic RGB-device with an known device-info type.
/// </summary>
public interface IRGBDevice<out TDeviceInfo> : IRGBDevice
where TDeviceInfo : IRGBDeviceInfo
{
/// <summary>
/// Gets generic information about the <see cref="IRGBDevice"/>.
/// </summary>
new TDeviceInfo DeviceInfo { get; }
}

View File

@ -1,49 +1,36 @@
using System;
namespace RGB.NET.Core;
namespace RGB.NET.Core
/// <summary>
/// Represents a generic information for a <see cref="IRGBDevice"/>
/// </summary>
public interface IRGBDeviceInfo
{
#region Properties & Fields
/// <summary>
/// Represents a generic information for a <see cref="IRGBDevice"/>
/// Gets the <see cref="RGBDeviceType"/> of the <see cref="IRGBDevice"/>.
/// </summary>
public interface IRGBDeviceInfo
{
#region Properties & Fields
RGBDeviceType DeviceType { get; }
/// <summary>
/// Gets the <see cref="RGBDeviceType"/> of the <see cref="IRGBDevice"/>.
/// </summary>
RGBDeviceType DeviceType { get; }
/// <summary>
/// Unique name of the <see cref="IRGBDevice"/>.
/// </summary>
string DeviceName { get; }
/// <summary>
/// Unique name of the <see cref="IRGBDevice"/>.
/// </summary>
string DeviceName { get; }
/// <summary>
/// Gets the manufacturer-name of the <see cref="IRGBDevice"/>.
/// </summary>
string Manufacturer { get; }
/// <summary>
/// Gets the manufacturer-name of the <see cref="IRGBDevice"/>.
/// </summary>
string Manufacturer { get; }
/// <summary>
/// Gets the model-name of the <see cref="IRGBDevice"/>.
/// </summary>
string Model { get; }
/// <summary>
/// Gets the model-name of the <see cref="IRGBDevice"/>.
/// </summary>
string Model { get; }
/// <summary>
/// Gets custom metadata added to the layout.
/// </summary>
object? LayoutMetadata { get; set; }
/// <summary>
/// Gets the lighting capability of the <see cref="IRGBDevice"/>
/// </summary>
RGBDeviceLighting Lighting { get; }
/// <summary>
/// Gets a bool indicating, if the <see cref="IRGBDevice"/> supports SynBacks or not.
/// </summary>
bool SupportsSyncBack { get; }
/// <summary>
/// Gets the URI of an image of the <see cref="IRGBDevice"/> or null if there is no image.
/// </summary>
Uri Image { get; set; }
#endregion
}
}
#endregion
}

View File

@ -1,48 +1,66 @@
using System;
// ReSharper disable EventNeverSubscribedTo.Global
using System;
using System.Collections.Generic;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Represents a generic device provider.
/// </summary>
public interface IRGBDeviceProvider : IDisposable
{
#region Properties & Fields
/// <summary>
/// Represents a generic device provider.
/// Indicates if the used SDK is initialized and ready to use.
/// </summary>
public interface IRGBDeviceProvider : IDisposable
{
#region Properties & Fields
bool IsInitialized { get; }
/// <summary>
/// Indicates if the used SDK is initialized and ready to use.
/// </summary>
bool IsInitialized { get; }
/// <summary>
/// Indicates if exceptions in the device provider are thrown or silently ignored.
/// </summary>
/// <remarks>
/// This should only be set to <c>true</c> for debugging/development purposes.
/// Production code should use the <see cref="Exception"/>-Event to handle exceptions.
/// </remarks>
bool ThrowsExceptions { get; }
/// <summary>
/// Gets a list of <see cref="IRGBDevice"/> loaded by this <see cref="IRGBDeviceProvider"/>.
/// </summary>
IEnumerable<IRGBDevice> Devices { get; }
/// <summary>
/// Gets a collection of <see cref="IRGBDevice"/> loaded by this <see cref="IRGBDeviceProvider"/>.
/// </summary>
IReadOnlyList<IRGBDevice> Devices { get; }
/// <summary>
/// Gets whether the application has exclusive access to devices or not.
/// </summary>
bool HasExclusiveAccess { get; }
/// <summary>
/// Gets a collection <see cref="IDeviceUpdateTrigger"/> registered to this device provider.
/// </summary>
IReadOnlyList<(int id, IDeviceUpdateTrigger trigger)> UpdateTriggers { get; }
#endregion
#endregion
#region Methods
#region Events
/// <summary>
/// Initializes the <see cref="IRGBDeviceProvider"/> if not already happened or reloads it if it is already initialized.
/// </summary>
/// <param name="loadFilter">Specifies which types of devices to load.</param>
/// <param name="exclusiveAccessIfPossible">Specifies whether the application should request exclusive access of possible or not.</param>
/// <param name="throwExceptions">Specifies whether exception during the initialization sequence should be thrown or not.</param>
/// <returns></returns>
bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool exclusiveAccessIfPossible = false, bool throwExceptions = false);
/// <summary>
/// Occurs when an exception is thrown in the device provider.
/// </summary>
event EventHandler<ExceptionEventArgs>? Exception;
/// <summary>
/// Resets all handled <see cref="IRGBDevice"/> back top default.
/// </summary>
void ResetDevices();
/// <summary>
/// Occures when the devices provided by this device provider changed.
/// </summary>
event EventHandler<DevicesChangedEventArgs>? DevicesChanged;
#endregion
}
}
#endregion
#region Methods
/// <summary>
/// Initializes the device provider and loads available devices.
/// </summary>
/// <param name="loadFilter"><see cref="RGBDeviceType"/>-flags to filter the devices to load.</param>
/// <param name="throwExceptions">Specifies if exceptions should be thrown or silently be ignored.</param>
/// <returns><c>true</c> if the initialization was successful; <c>false</c> otherwise.</returns>
bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false);
#endregion
}

View File

@ -1,20 +0,0 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a generic device provider loaded used to dynamically load devices into an application.
/// This class should always provide an empty public constructor!
/// </summary>
public interface IRGBDeviceProviderLoader
{
/// <summary>
/// Indicates if the returned device-provider needs some specific initialization before use.
/// </summary>
bool RequiresInitialization { get; }
/// <summary>
/// Gets the device-provider.
/// </summary>
/// <returns>The device-provider.</returns>
IRGBDeviceProvider GetDeviceProvider();
}
}

View File

@ -1,11 +0,0 @@
using System.Collections.Generic;
namespace RGB.NET.Core
{
/// <inheritdoc />
/// <summary>
/// Represents a special part of a <see cref="T:RGB.NET.Core.IRGBDevice" />.
/// </summary>
public interface IRGBDeviceSpecialPart : IEnumerable<Led>
{ }
}

View File

@ -0,0 +1,17 @@
// ReSharper disable InconsistentNaming
#pragma warning disable 1591
namespace RGB.NET.Core;
/// <summary>
/// Contains a list of available keyboard layout types.
/// </summary>
public enum KeyboardLayoutType
{
Unknown = 0,
ANSI = 1,
ISO = 2,
JIS = 3,
ABNT = 4,
KS = 5
}

View File

@ -1,151 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml.Serialization;
namespace RGB.NET.Core.Layout
{
/// <summary>
/// Represents the serializable layout of a <see cref="IRGBDevice"/>.
/// </summary>
[Serializable]
[XmlRoot("Device")]
public class DeviceLayout
{
#region Properties & Fields
/// <summary>
/// Gets or sets the name of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the description of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Description")]
public string Description { get; set; }
/// <summary>
/// Gets or sets the <see cref="RGBDeviceType"/> of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Type")]
public RGBDeviceType Type { get; set; }
/// <summary>
/// Gets or sets the <see cref="RGBDeviceLighting"/> of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Lighting")]
public RGBDeviceLighting Lighting { get; set; }
/// <summary>
/// Gets or sets the vendor of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Vendor")]
public string Vendor { get; set; }
/// <summary>
/// Gets or sets the model of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Model")]
public string Model { get; set; }
/// <summary>
/// Gets or sets the <see cref="Core.Shape"/> of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Shape")]
[DefaultValue(Shape.Rectangle)]
public Shape Shape { get; set; } = Shape.Rectangle;
/// <summary>
/// Gets or sets the width of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Width")]
public double Width { get; set; }
/// <summary>
/// Gets or sets the height of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlElement("Height")]
public double Height { get; set; }
/// <summary>
/// Gets or sets the width of one 'unit' used for the calculation of led positions and sizes.
/// </summary>
[XmlElement("LedUnitWidth")]
[DefaultValue(19.0)]
public double LedUnitWidth { get; set; } = 19.0;
/// <summary>
/// Gets or sets the height of one 'unit' used for the calculation of led positions and sizes.
/// </summary>
[XmlElement("LedUnitHeight")]
[DefaultValue(19.0)]
public double LedUnitHeight { get; set; } = 19.0;
/// <summary>
/// The path images for this device are collected in.
/// </summary>
[XmlElement("ImageBasePath")]
public string ImageBasePath { get; set; }
/// <summary>
/// The image file for this device.
/// </summary>
[XmlElement("DeviceImage")]
public string DeviceImage { get; set; }
/// <summary>
/// Gets or sets a list of <see cref="LedLayout"/> representing all the <see cref="Led"/> of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlArray("Leds")]
public List<LedLayout> Leds { get; set; } = new List<LedLayout>();
/// <summary>
/// Gets or sets a list of <see cref="LedImageLayout"/> representing the layouts for the images of all the <see cref="Led"/> of the <see cref="DeviceLayout"/>.
/// </summary>
[XmlArray("LedImageLayouts")]
public List<LedImageLayout> LedImageLayouts { get; set; } = new List<LedImageLayout>();
#endregion
#region Methods
/// <summary>
/// Creates a new <see cref="DeviceLayout"/> from the given xml.
/// </summary>
/// <param name="path">The path to the xml file.</param>
/// <returns>The deserialized <see cref="DeviceLayout"/>.</returns>
public static DeviceLayout Load(string path)
{
if (!File.Exists(path)) return null;
try
{
XmlSerializer serializer = new XmlSerializer(typeof(DeviceLayout));
using (StreamReader reader = new StreamReader(path))
{
DeviceLayout layout = serializer.Deserialize(reader) as DeviceLayout;
if (layout?.Leds != null)
{
LedLayout lastLed = null;
foreach (LedLayout led in layout.Leds)
{
led.CalculateValues(layout, lastLed);
lastLed = led;
}
}
return layout;
}
}
catch
{
return null;
}
}
#endregion
}
}

View File

@ -1,25 +0,0 @@
using System;
using System.Xml.Serialization;
namespace RGB.NET.Core.Layout
{
/// <summary>
/// Represents the serializable image-data of a specific <see cref="Led"/>.
/// </summary>
[Serializable]
[XmlRoot("LedImage")]
public class LedImage
{
/// <summary>
/// Gets or sets the Id of the <see cref="LedImage"/>.
/// </summary>
[XmlAttribute("Id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the image of the <see cref="LedImage"/>.
/// </summary>
[XmlAttribute("Image")]
public string Image { get; set; }
}
}

View File

@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml.Serialization;
namespace RGB.NET.Core.Layout
{
/// <summary>
/// Represents the serializable collection of <see cref="LedImage"/> for a specific layout.
/// </summary>
[Serializable]
[XmlRoot("LedImageLayout")]
public class LedImageLayout
{
/// <summary>
/// Gets or sets the layout of the <see cref="LedImage"/>.
/// </summary>
[XmlAttribute("Layout")]
[DefaultValue(null)]
public string Layout { get; set; }
/// <summary>
/// Gets or sets a list of <see cref="LedImage"/> representing the images of all the <see cref="Led"/> of the represented layout.
/// </summary>
[XmlArray("LedImages")]
public List<LedImage> LedImages { get; set; } = new List<LedImage>();
}
}

View File

@ -1,182 +0,0 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Xml.Serialization;
namespace RGB.NET.Core.Layout
{
/// <summary>
/// Represents the serializable layout of a <see cref="Led"/>.
/// </summary>
[Serializable]
[XmlType("Led")]
public class LedLayout
{
#region Properties & Fields
/// <summary>
/// Gets or sets the Id of the <see cref="LedLayout"/>.
/// </summary>
[XmlAttribute("Id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the descriptive <see cref="RGB.NET.Core.Shape"/> of the <see cref="LedLayout"/>.
/// This property is for XML-serialization only and should not be directly accessed.
/// </summary>
[XmlElement("Shape")]
[DefaultValue("Rectangle")]
public string DescriptiveShape { get; set; } = "Rectangle";
/// <summary>
/// Gets or sets the descriptive x-position of the <see cref="LedLayout"/>.
/// This property is for XML-serialization only and should not be directly accessed.
/// </summary>
[XmlElement("X")]
[DefaultValue("+")]
public string DescriptiveX { get; set; } = "+";
/// <summary>
/// Gets or sets the descriptive y-position of the <see cref="LedLayout"/>.
/// This property is for XML-serialization only and should not be directly accessed.
/// </summary>
[XmlElement("Y")]
[DefaultValue("=")]
public string DescriptiveY { get; set; } = "=";
/// <summary>
/// Gets or sets the descriptive width of the <see cref="LedLayout"/>.
/// This property is for XML-serialization only and should not be directly accessed.
/// </summary>
[XmlElement("Width")]
[DefaultValue("1.0")]
public string DescriptiveWidth { get; set; } = "1.0";
/// <summary>
/// Gets or sets the descriptive height of the <see cref="LedLayout"/>.
/// This property is for XML-serialization only and should not be directly accessed.
/// </summary>
[XmlElement("Height")]
[DefaultValue("1.0")]
public string DescriptiveHeight { get; set; } = "1.0";
/// <summary>
/// Gets or sets the <see cref="RGB.NET.Core.Shape"/> of the <see cref="LedLayout"/>.
/// </summary>
[XmlIgnore]
public Shape Shape { get; set; }
/// <summary>
/// Gets or sets the vecor-data representing a custom-shape of the <see cref="LedLayout"/>.
/// </summary>
[XmlIgnore]
public string ShapeData { get; set; }
/// <summary>
/// Gets or sets the x-position of the <see cref="LedLayout"/>.
/// </summary>
[XmlIgnore]
public double X { get; private set; }
/// <summary>
/// Gets or sets the y-position of the <see cref="LedLayout"/>.
/// </summary>
[XmlIgnore]
public double Y { get; private set; }
/// <summary>
/// Gets or sets the width of the <see cref="LedLayout"/>.
/// </summary>
[XmlIgnore]
public double Width { get; private set; }
/// <summary>
/// Gets or sets the height of the <see cref="LedLayout"/>.
/// </summary>
[XmlIgnore]
public double Height { get; private set; }
#endregion
#region Methods
/// <summary>
/// Calculates the position- and size-data from the respective descriptive values.
/// </summary>
/// <param name="device">The <see cref="DeviceLayout"/> this <see cref="LedLayout"/> belongs to.</param>
/// <param name="lastLed">The <see cref="LedLayout"/> previously calculated.</param>
public void CalculateValues(DeviceLayout device, LedLayout lastLed)
{
if (!Enum.TryParse(DescriptiveShape, true, out Shape shape))
{
shape = Shape.Custom;
ShapeData = DescriptiveShape;
}
Shape = shape;
Width = GetSizeValue(DescriptiveWidth, device.LedUnitWidth);
Height = GetSizeValue(DescriptiveHeight, device.LedUnitHeight);
X = GetLocationValue(DescriptiveX, lastLed?.X ?? 0, Width, lastLed?.Width ?? 0);
Y = GetLocationValue(DescriptiveY, lastLed?.Y ?? 0, Height, lastLed?.Height ?? 0);
}
private double GetLocationValue(string value, double lastValue, double currentSize, double lastSize)
{
try
{
if (string.IsNullOrWhiteSpace(value)) return 0;
value = value.Replace(" ", string.Empty);
if (string.Equals(value, "=", StringComparison.Ordinal))
return lastValue;
if (string.Equals(value, "+", StringComparison.Ordinal))
return lastValue + lastSize;
if (value.StartsWith("+", StringComparison.Ordinal))
return lastValue + lastSize + double.Parse(value.Substring(1), CultureInfo.InvariantCulture);
if (string.Equals(value, "-", StringComparison.Ordinal))
return lastValue - currentSize;
if (value.StartsWith("-", StringComparison.Ordinal))
return lastValue - currentSize - double.Parse(value.Substring(1), CultureInfo.InvariantCulture);
if (string.Equals(value, "~", StringComparison.Ordinal))
return (lastValue + lastSize) - currentSize;
if (value.StartsWith("~", StringComparison.Ordinal))
return (lastValue + lastSize) - currentSize - double.Parse(value.Substring(1), CultureInfo.InvariantCulture);
return double.Parse(value, CultureInfo.InvariantCulture);
}
catch
{
return 0;
}
}
private double GetSizeValue(string value, double unitSize)
{
try
{
if (string.IsNullOrWhiteSpace(value)) return 0;
value = value.Replace(" ", string.Empty);
if (value.EndsWith("mm", StringComparison.OrdinalIgnoreCase))
return double.Parse(value.Substring(0, value.Length - 2), CultureInfo.InvariantCulture);
return unitSize * double.Parse(value, CultureInfo.InvariantCulture);
}
catch
{
return 0;
}
}
#endregion
}
}

View File

@ -1,25 +0,0 @@
using RGB.NET.Core.Layout;
namespace RGB.NET.Core
{
/// <summary>
/// Contains a list of different lightning-modes used by <see cref="DeviceLayout"/>.
/// </summary>
public enum RGBDeviceLighting
{
/// <summary>
/// The <see cref="IRGBDevice"/> doesn't support lighting,
/// </summary>
None = 0,
/// <summary>
/// The <see cref="IRGBDevice"/> supports per-key-lightning.
/// </summary>
Key = 1,
/// <summary>
/// The <see cref="IRGBDevice"/> supports per-device-lightning.
/// </summary>
Device = 2,
}
}

View File

@ -1,96 +1,110 @@
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Contains a list of different types of device.
/// </summary>
[Flags]
public enum RGBDeviceType
{
/// <summary>
/// Contains a list of different types of device.
/// Represents nothing.
/// </summary>
[Flags]
public enum RGBDeviceType
{
/// <summary>
/// Represents nothing.
/// </summary>
None = 0,
None = 0,
/// <summary>
/// Represents a keyboard.
/// </summary>
Keyboard = 1 << 0,
/// <summary>
/// Represents a keyboard.
/// </summary>
Keyboard = 1 << 0,
/// <summary>
/// Represents a mouse.
/// </summary>
Mouse = 1 << 1,
/// <summary>
/// Represents a mouse.
/// </summary>
Mouse = 1 << 1,
/// <summary>
/// Represents a headset.
/// </summary>
Headset = 1 << 2,
/// <summary>
/// Represents a headset.
/// </summary>
Headset = 1 << 2,
/// <summary>
/// Represents a mousepad.
/// </summary>
Mousepad = 1 << 3,
/// <summary>
/// Represents a mousepad.
/// </summary>
Mousepad = 1 << 3,
/// <summary>
/// Represents a LED-stipe.
/// </summary>
LedStripe = 1 << 4,
/// <summary>
/// Represents a LED-stipe.
/// </summary>
LedStripe = 1 << 4,
/// <summary>
/// Represents a LED-matrix.
/// </summary>
LedMatrix = 1 << 5,
/// <summary>
/// Represents a LED-matrix.
/// </summary>
LedMatrix = 1 << 5,
/// <summary>
/// Represents a Mainboard.
/// </summary>
Mainboard = 1 << 6,
/// <summary>
/// Represents a Mainboard.
/// </summary>
Mainboard = 1 << 6,
/// <summary>
/// Represents a Graphics card.
/// </summary>
GraphicsCard = 1 << 7,
/// <summary>
/// Represents a Graphics card.
/// </summary>
GraphicsCard = 1 << 7,
/// <summary>
/// Represents a DRAM-bank.
/// </summary>
DRAM = 1 << 8,
/// <summary>
/// Represents a DRAM-bank.
/// </summary>
DRAM = 1 << 8,
/// <summary>
/// Represents a headset stand.
/// </summary>
HeadsetStand = 1 << 9,
/// <summary>
/// Represents a headset stand.
/// </summary>
HeadsetStand = 1 << 9,
/// <summary>
/// Represents a keypad.
/// </summary>
Keypad = 1 << 10,
/// <summary>
/// Represents a keypad.
/// </summary>
Keypad = 1 << 10,
/// <summary>
/// Represents a fan.
/// </summary>
Fan = 1 << 11,
/// <summary>
/// Represents a fan.
/// </summary>
Fan = 1 << 11,
/// <summary>
/// Represents a speaker
/// </summary>
Speaker = 1 << 12,
/// <summary>
/// Represents a speaker
/// </summary>
Speaker = 1 << 12,
/// <summary>
/// Represents a cooler.
/// </summary>
Cooler = 1 << 13,
/// <summary>
/// Represents a cooler.
/// </summary>
Cooler = 1 << 13,
/// <summary>
/// Represents a device where the type is not known or not present in the list.
/// </summary>
Unknown = 1 << 31,
/// <summary>
/// Represents a monitor.
/// </summary>
Monitor = 1 << 14,
/// <summary>
/// Represents all devices.
/// </summary>
All = ~None
}
}
/// <summary>
/// Represents a generic led-controller.
/// </summary>
LedController = 1 << 15,
/// <summary>
/// Represents a game controller.
/// </summary>
GameController = 1 << 16,
/// <summary>
/// Represents a device where the type is not known or not present in the list.
/// </summary>
Unknown = 1 << 31,
/// <summary>
/// Represents all devices.
/// </summary>
All = ~None
}

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a cooler-device
/// </summary>
public interface ICooler : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a cooler-device
/// </summary>
public interface ICooler : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a DRAM-device
/// </summary>
public interface IDRAM : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a DRAM-device
/// </summary>
public interface IDRAM : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// represents a fan-device
/// </summary>
public interface IFan : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// represents a fan-device
/// </summary>
public interface IFan : IRGBDevice;

View File

@ -0,0 +1,6 @@
namespace RGB.NET.Core;
/// <summary>
/// Represents a gamecontroller-device
/// </summary>
public interface IGameController: IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a graphics-card-device
/// </summary>
public interface IGraphicsCard : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a graphics-card-device
/// </summary>
public interface IGraphicsCard : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a headset-device
/// </summary>
public interface IHeadset : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a headset-device
/// </summary>
public interface IHeadset : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a headset-stand-device
/// </summary>
public interface IHeadsetStand : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a headset-stand-device
/// </summary>
public interface IHeadsetStand : IRGBDevice;

View File

@ -1,8 +1,23 @@
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Represents a generic keyboard-device.
/// </summary>
public interface IKeyboard : IRGBDevice
{
/// <summary>
/// Represents a keyboard-device
/// Gets the device information assiciated with this device.
/// </summary>
public interface IKeyboard : IRGBDevice
{ }
new IKeyboardDeviceInfo DeviceInfo { get; }
}
/// <summary>
/// Represents a generic keyboard device information.
/// </summary>
public interface IKeyboardDeviceInfo : IRGBDeviceInfo
{
/// <summary>
/// Gets the <see cref="KeyboardLayoutType"/> of the keyboard.
/// </summary>
KeyboardLayoutType Layout { get; }
}

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a keypad-device
/// </summary>
public interface IKeypad : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a keypad-device
/// </summary>
public interface IKeypad : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a led-matrix-device
/// </summary>
public interface ILedMatrix : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a led-matrix-device
/// </summary>
public interface ILedMatrix : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a led-stripe-device
/// </summary>
public interface ILedStripe : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a led-stripe-device
/// </summary>
public interface ILedStripe : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a mainboard-device
/// </summary>
public interface IMainboard : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a mainboard-device
/// </summary>
public interface IMainboard : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a mouse-device
/// </summary>
public interface IMouse : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a mouse-device
/// </summary>
public interface IMouse : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a mousepad-device
/// </summary>
public interface IMousepad : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a mousepad-device
/// </summary>
public interface IMousepad : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a speaker-device
/// </summary>
public interface ISpeaker : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a speaker-device
/// </summary>
public interface ISpeaker : IRGBDevice;

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core
{
/// <summary>
/// Represents a device with unkown or not specified type.
/// </summary>
public interface IUnknownDevice : IRGBDevice
{ }
}
namespace RGB.NET.Core;
/// <summary>
/// Represents a device with unkown or not specified type.
/// </summary>
public interface IUnknownDevice : IRGBDevice;

View File

@ -0,0 +1,27 @@
using System;
namespace RGB.NET.Core;
public sealed class DevicesChangedEventArgs(IRGBDevice device, DevicesChangedEventArgs.DevicesChangedAction action)
: EventArgs
{
#region Properties & Fields
public IRGBDevice Device { get; } = device;
public DevicesChangedAction Action { get; } = action;
#endregion
#region Methods
public static DevicesChangedEventArgs CreateDevicesAddedArgs(IRGBDevice addedDevice) => new(addedDevice, DevicesChangedAction.Added);
public static DevicesChangedEventArgs CreateDevicesRemovedArgs(IRGBDevice removedDevice) => new(removedDevice, DevicesChangedAction.Removed);
#endregion
public enum DevicesChangedAction
{
Added,
Removed
}
}

View File

@ -3,35 +3,48 @@
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Exception" />-event.
/// </summary>
public class ExceptionEventArgs : EventArgs
{
#region Properties & Fields
/// <summary>
/// Gets the <see cref="System.Exception"/> which is responsible for the event-call.
/// </summary>
public Exception Exception { get; }
/// <summary>
/// Gets a bool indicating if the exception is critical for the thrower.
/// </summary>
public bool IsCritical { get; }
/// <summary>
/// Gets or sets if the exception should be thrown after the event is handled.
/// </summary>
public bool Throw { get; set; }
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Exception" />-event.
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.ExceptionEventArgs" /> class.
/// </summary>
public class ExceptionEventArgs : EventArgs
/// <param name="exception">The <see cref="T:System.Exception" /> which is responsible for the event-call.</param>
/// <param name="isCritical">Indicates if the exception is critical for the thrower.</param>
/// <param name="throw">Indicates if the exception should be thrown after the event is handled.</param>
public ExceptionEventArgs(Exception exception, bool isCritical = false, bool @throw = false)
{
#region Properties & Fields
/// <summary>
/// Gets the <see cref="System.Exception"/> which is responsible for the event-call.
/// </summary>
public Exception Exception { get; }
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.ExceptionEventArgs" /> class.
/// </summary>
/// <param name="exception">The <see cref="T:System.Exception" /> which is responsible for the event-call.</param>
public ExceptionEventArgs(Exception exception)
{
this.Exception = exception;
}
#endregion
this.Exception = exception;
this.IsCritical = isCritical;
this.Throw = @throw;
}
}
#endregion
}

View File

@ -1,64 +0,0 @@
using System;
namespace RGB.NET.Core
{
public class ResolvePathEventArgs : EventArgs
{
#region Properties & Fields
/// <summary>
/// Gets the filename used to resolve the path.
/// This has to be checked for null since it'S possible that only <see cref="FileName"/> is used.
/// Also check <see cref="RelativePath "/> before use.
/// </summary>
public string RelativePart { get; }
/// <summary>
/// Gets the filename used to resolve the path.
/// This has to be checked for null since it'S possible that only <see cref="RelativePart"/> is used.
/// Also check <see cref="RelativePath "/> before use.
/// </summary>
public string FileName { get; }
/// <summary>
/// Gets the relative path used to resolve the path.
/// If this is set <see cref="RelativePart" /> and <see cref="FileName" /> are unused.
/// </summary>
public string RelativePath { get; }
/// <summary>
/// Gets or sets the resolved path.
/// </summary>
public string FinalPath { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Corer.ResolvePathEventArgs" /> class.
/// </summary>
/// <param name="relativePart">The filename used to resolve the path.</param>
/// <param name="fileName">The filename used to resolve the path.</param>
/// <param name="finalPath">The relative part used to resolve the path.</param>
public ResolvePathEventArgs(string relativePart, string fileName, string finalPath)
{
this.RelativePart = relativePart;
this.FileName = fileName;
this.FinalPath = finalPath;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Corer.ResolvePathEventArgs" /> class.
/// </summary>
/// <param name="relativePath">The relative path used to resolve the path.</param>
/// <param name="finalPath">The relative part used to resolve the path.</param>
public ResolvePathEventArgs(string relativePath, string finalPath)
{
this.RelativePath = relativePath;
this.FinalPath = finalPath;
}
#endregion
}
}

View File

@ -2,51 +2,65 @@
// ReSharper disable UnusedAutoPropertyAccessor.Global
using System;
using System.Collections.Generic;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.SurfaceLayoutChanged" />-event.
/// </summary>
public class SurfaceLayoutChangedEventArgs : EventArgs
{
#region Properties & Fields
/// <summary>
/// Gets the <see cref="IRGBDevice"/> that caused the change. Returns null if the change isn't caused by a <see cref="IRGBDevice"/>.
/// </summary>
public IRGBDevice? Devices { get; }
/// <summary>
/// Gets a value indicating if the event is caused by the addition of a new <see cref="IRGBDevice"/> to the <see cref="RGBSurface"/>.
/// </summary>
public bool DeviceAdded { get; }
/// <summary>
/// Gets a value indicating if the event is caused by the removal of a <see cref="IRGBDevice"/> to the <see cref="RGBSurface"/>.
/// </summary>
public bool DeviceRemoved { get; }
/// <summary>
/// Gets a value indicating if the event is caused by a changed location or size of one of the <see cref="IRGBDevice"/> on the <see cref="RGBSurface"/>.
/// </summary>
public bool DeviceChanged { get; }
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.SurfaceLayoutChanged" />-event.
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.SurfaceLayoutChangedEventArgs" /> class.
/// </summary>
public class SurfaceLayoutChangedEventArgs : EventArgs
/// <param name="devices">The <see cref="T:RGB.NET.Core.IRGBDevice" /> that caused the change.</param>
/// <param name="deviceAdded">A value indicating if the event is caused by the addition of a new <see cref="T:RGB.NET.Core.IRGBDevice" /> to the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
/// <param name="deviceRemoved">A value indicating if the event is caused by the removal of a <see cref="T:RGB.NET.Core.IRGBDevice" /> from the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
/// <param name="deviceChanged">A value indicating if the event is caused by a change to a <see cref="T:RGB.NET.Core.IRGBDevice" /> on the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
private SurfaceLayoutChangedEventArgs(IRGBDevice? devices, bool deviceAdded, bool deviceRemoved, bool deviceChanged)
{
#region Properties & Fields
/// <summary>
/// Gets the <see cref="IRGBDevice"/> that caused the change. Returns null if the change isn't caused by a <see cref="IRGBDevice"/>.
/// </summary>
public IEnumerable<IRGBDevice> Devices { get; }
/// <summary>
/// Gets a value indicating if the event is caused by the addition of a new <see cref="IRGBDevice"/> to the <see cref="RGBSurface"/>.
/// </summary>
public bool DeviceAdded { get; }
/// <summary>
/// Gets a value indicating if the event is caused by a changed location of one of the devices on the <see cref="RGBSurface"/>.
/// </summary>
public bool DeviceLocationChanged { get; }
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.SurfaceLayoutChangedEventArgs" /> class.
/// </summary>
/// <param name="devices">The <see cref="T:RGB.NET.Core.IRGBDevice" /> that caused the change.</param>
/// <param name="deviceAdded">A value indicating if the event is caused by the addition of a new <see cref="T:RGB.NET.Core.IRGBDevice" /> to the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
/// <param name="deviceLocationChanged">A value indicating if the event is caused by a changed location of one of the devices on the <see cref="T:RGB.NET.Core.RGBSurface" />.</param>
public SurfaceLayoutChangedEventArgs(IEnumerable<IRGBDevice> devices, bool deviceAdded, bool deviceLocationChanged)
{
this.Devices = devices;
this.DeviceAdded = deviceAdded;
this.DeviceLocationChanged = deviceLocationChanged;
}
#endregion
this.Devices = devices;
this.DeviceAdded = deviceAdded;
this.DeviceRemoved = deviceRemoved;
this.DeviceChanged = deviceChanged;
}
}
#endregion
#region Factory
internal static SurfaceLayoutChangedEventArgs FromAddedDevice(IRGBDevice device) => new(device, true, false, false);
internal static SurfaceLayoutChangedEventArgs FromRemovedDevice(IRGBDevice device) => new(device, false, true, false);
internal static SurfaceLayoutChangedEventArgs FromChangedDevice(IRGBDevice device) => new(device, false, false, true);
internal static SurfaceLayoutChangedEventArgs Misc() => new(null, false, false, false);
#endregion
}

View File

@ -1,11 +1,9 @@
using System;
namespace RGB.NET.Core
{
/// <inheritdoc />
/// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updated" />-event.
/// </summary>
public class UpdatedEventArgs : EventArgs
{ }
}
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updated" />-event.
/// </summary>
public class UpdatedEventArgs : EventArgs;

View File

@ -3,49 +3,48 @@
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updating" />-event.
/// </summary>
public class UpdatingEventArgs : EventArgs
{
#region Properties & Fields
/// <summary>
/// Gets the elapsed time (in seconds) since the last update.
/// </summary>
public double DeltaTime { get; }
/// <summary>
/// Gets the trigger causing this update.
/// </summary>
public IUpdateTrigger? Trigger { get; }
/// <summary>
/// Gets the custom-data provided by the trigger for this update.
/// </summary>
public ICustomUpdateData CustomData { get; }
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updating" />-event.
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.UpdatingEventArgs" /> class.
/// </summary>
public class UpdatingEventArgs : EventArgs
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
/// <param name="trigger">The trigger causing this update.</param>
/// <param name="customData">The custom-data provided by the trigger for this update.</param>
public UpdatingEventArgs(double deltaTime, IUpdateTrigger? trigger, ICustomUpdateData customData)
{
#region Properties & Fields
/// <summary>
/// Gets the elapsed time (in seconds) since the last update.
/// </summary>
public double DeltaTime { get; }
/// <summary>
/// Gets the trigger causing this update.
/// </summary>
public IUpdateTrigger Trigger { get; }
/// <summary>
/// Gets the custom-data provided by the trigger for this update.
/// </summary>
public CustomUpdateData CustomData { get; }
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.UpdatingEventArgs" /> class.
/// </summary>
/// <param name="deltaTime">The elapsed time (in seconds) since the last update.</param>
/// <param name="trigger">The trigger causing this update.</param>
/// <param name="customData">The custom-data provided by the trigger for this update.</param>
public UpdatingEventArgs(double deltaTime, IUpdateTrigger trigger, CustomUpdateData customData)
{
this.DeltaTime = deltaTime;
this.Trigger = trigger;
this.CustomData = customData;
}
#endregion
this.DeltaTime = deltaTime;
this.Trigger = trigger;
this.CustomData = customData;
}
}
#endregion
}

View File

@ -0,0 +1,34 @@
using System;
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents an exception thrown by a <see cref="IRGBDeviceProvider" />.
/// </summary>
public class DeviceProviderException : ApplicationException
{
#region Properties & Fields
/// <summary>
/// Gets a bool indicating if the exception is critical and shouldn't be ingored.
/// </summary>
public bool IsCritical { get; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="DeviceProviderException" /> class.
/// </summary>
/// <param name="innerException">The exception that is the casue of the current exception or null if this exception was thrown on purpose.</param>
/// <param name="isCritical">A value indicating if the exception is critical and shouldn't be ignored.</param>
public DeviceProviderException(Exception? innerException, bool isCritical)
: base(innerException?.Message, innerException)
{
this.IsCritical = isCritical;
}
#endregion
}

View File

@ -1,25 +1,24 @@
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents an exception thrown by an <see cref="T:RGB.NET.Core.IRGBDevice" />.
/// </summary>
public class RGBDeviceException : ApplicationException
{
#region Constructors
/// <inheritdoc />
/// <summary>
/// Represents an exception thrown by an <see cref="T:RGB.NET.Core.IRGBDevice" />.
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.RGBDeviceException" /> class.
/// </summary>
public class RGBDeviceException : ApplicationException
{
#region Constructors
/// <param name="message">The message which describes the reason of throwing this exception.</param>
/// <param name="innerException">Optional inner exception, which lead to this exception.</param>
public RGBDeviceException(string message, Exception? innerException = null)
: base(message, innerException)
{ }
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.RGBDeviceException" /> class.
/// </summary>
/// <param name="message">The message which describes the reason of throwing this exception.</param>
/// <param name="innerException">Optional inner exception, which lead to this exception.</param>
public RGBDeviceException(string message, Exception innerException = null)
: base(message, innerException)
{ }
#endregion
}
}
#endregion
}

View File

@ -0,0 +1,24 @@
using System;
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents an exception thrown by an <see cref="T:RGB.NET.Core.RGBSurface" />.
/// </summary>
public class RGBSurfaceException : ApplicationException
{
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Core.RGBSurfaceException" /> class.
/// </summary>
/// <param name="message">The message which describes the reason of throwing this exception.</param>
/// <param name="innerException">Optional inner exception, which lead to this exception.</param>
public RGBSurfaceException(string message, Exception? innerException = null)
: base(message, innerException)
{ }
#endregion
}

View File

@ -1,30 +1,32 @@
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Offers some extensions and helper-methods for <see cref="Color"/> related things.
/// </summary>
public static class ColorExtensions
{
public static class ColorExtensions
#region Methods
/// <summary>
/// Calculates the distance between the two specified colors using the redmean algorithm.
/// For more infos check https://www.compuphase.com/cmetric.htm
/// </summary>
/// <param name="color1">The start color of the distance calculation.</param>
/// <param name="color2">The end color fot the distance calculation.</param>
/// <returns>The redmean distance between the two specified colors.</returns>
public static double DistanceTo(this Color color1, Color color2)
{
#region Methods
(_, byte r1, byte g1, byte b1) = color1.GetRGBBytes();
(_, byte r2, byte g2, byte b2) = color2.GetRGBBytes();
/// <summary>
/// Calculates the distance between the two given colors using the redmean algorithm.
/// For more infos check https://www.compuphase.com/cmetric.htm
/// </summary>
/// <param name="color1">The start color of the distance calculation.</param>
/// <param name="color2">The end color fot the distance calculation.</param>
/// <returns></returns>
public static double DistanceTo(this Color color1, Color color2)
{
(_, byte r1, byte g1, byte b1) = color1.GetRGBBytes();
(_, byte r2, byte g2, byte b2) = color2.GetRGBBytes();
long rmean = (r1 + r2) / 2;
long r = r1 - r2;
long g = g1 - g2;
long b = b1 - b2;
return Math.Sqrt((((512 + rmean) * r * r) >> 8) + (4 * g * g) + (((767 - rmean) * b * b) >> 8));
}
#endregion
long rmean = (r1 + r2) / 2;
long r = r1 - r2;
long g = g1 - g2;
long b = b1 - b2;
return Math.Sqrt((((512 + rmean) * r * r) >> 8) + (4 * g * g) + (((767 - rmean) * b * b) >> 8));
}
}
#endregion
}

View File

@ -0,0 +1,55 @@
namespace RGB.NET.Core;
/// <summary>
/// Offers some extensions for easier use of <see cref="CustomUpdateData"/>.
/// </summary>
public static class CustomUpdateDataExtension
{
/// <summary>
/// Sets the <see cref="CustomUpdateDataIndex.FLUSH_LEDS"/>-Parameter to the given value.
/// </summary>
/// <param name="customUpdateData">The update-data to modify.</param>
/// <param name="value">The value to set.</param>
/// <returns>The modified update-data.</returns>
public static CustomUpdateData FlushLeds(this CustomUpdateData customUpdateData, bool value = true)
{
customUpdateData[CustomUpdateDataIndex.FLUSH_LEDS] = value;
return customUpdateData;
}
/// <summary>
/// Sets the <see cref="CustomUpdateDataIndex.RENDER"/>-Parameter to the given value.
/// </summary>
/// <param name="customUpdateData">The update-data to modify.</param>
/// <param name="value">The value to set.</param>
/// <returns>The modified update-data.</returns>
public static CustomUpdateData Render(this CustomUpdateData customUpdateData, bool value = true)
{
customUpdateData[CustomUpdateDataIndex.RENDER] = value;
return customUpdateData;
}
/// <summary>
/// Sets the <see cref="CustomUpdateDataIndex.UPDATE_DEVICES"/>-Parameter to the given value.
/// </summary>
/// <param name="customUpdateData">The update-data to modify.</param>
/// <param name="value">The value to set.</param>
/// <returns>The modified update-data.</returns>
public static CustomUpdateData UpdateDevices(this CustomUpdateData customUpdateData, bool value = true)
{
customUpdateData[CustomUpdateDataIndex.UPDATE_DEVICES] = value;
return customUpdateData;
}
/// <summary>
/// Sets the <see cref="CustomUpdateDataIndex.HEARTBEAT"/>-Parameter to the given value.
/// </summary>
/// <param name="customUpdateData">The update-data to modify.</param>
/// <param name="value">The value to set.</param>
/// <returns>The modified update-data.</returns>
public static CustomUpdateData Heartbeat(this CustomUpdateData customUpdateData, bool value = true)
{
customUpdateData[CustomUpdateDataIndex.HEARTBEAT] = value;
return customUpdateData;
}
}

View File

@ -1,101 +1,111 @@
using System;
using System.Runtime.CompilerServices;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Offers some extensions and helper-methods for the work with floats.
/// </summary>
public static class FloatExtensions
{
#region Constants
/// <summary>
/// Offers some extensions and helper-methods for the work with doubles
/// Defines the precision RGB.NET processes floating point comparisons in.
/// </summary>
public static class DoubleExtensions
public const float TOLERANCE = 1E-7f;
#endregion
#region Methods
/// <summary>
/// Checks if two values are equal respecting the <see cref="TOLERANCE"/>.
/// </summary>
/// <param name="value1">The first value to compare.</param>
/// <param name="value2">The first value to compare.</param>
/// <returns><c>true</c> if the difference is smaller than the <see cref="TOLERANCE"/>; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool EqualsInTolerance(this float value1, float value2) => Math.Abs(value1 - value2) < TOLERANCE;
/// <summary>
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The lower value of the range the value is clamped to.</param>
/// <param name="max">The higher value of the range the value is clamped to.</param>
/// <returns>The clamped value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Clamp(this float value, float min, float max)
{
#region Constants
/// <summary>
/// Defines the precision RGB.NET processes floating point comparisons in.
/// </summary>
public const double TOLERANCE = 1E-10;
#endregion
#region Methods
/// <summary>
/// Checks if two values are equal respecting the <see cref="TOLERANCE"/>.
/// </summary>
/// <param name="value1">The first value to compare.</param>
/// <param name="value2">The first value to compare.</param>
/// <returns><c>true</c> if the difference is smaller than the <see cref="TOLERANCE"/>; otherwise, <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool EqualsInTolerance(this double value1, double value2) => Math.Abs(value1 - value2) < TOLERANCE;
/// <summary>
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The lower value of the range the value is clamped to.</param>
/// <param name="max">The higher value of the range the value is clamped to.</param>
/// <returns>The clamped value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Clamp(this double value, double min, double max)
{
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
if (value < min) return min;
if (value > max) return max;
return value;
// ReSharper restore ConvertIfStatementToReturnStatement
}
/// <summary>
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The lower value of the range the value is clamped to.</param>
/// <param name="max">The higher value of the range the value is clamped to.</param>
/// <returns>The clamped value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Clamp(this int value, int min, int max)
{
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
if (value < min) return min;
if (value > max) return max;
return value;
// ReSharper restore ConvertIfStatementToReturnStatement
}
/// <summary>
/// Enforces the provided value to be in the specified range by wrapping it around the edges if it exceeds them.
/// </summary>
/// <param name="value">The value to wrap.</param>
/// <param name="min">The lower value of the range the value is wrapped into.</param>
/// <param name="max">The higher value of the range the value is wrapped into.</param>
/// <returns>The wrapped value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Wrap(this double value, double min, double max)
{
double range = max - min;
while (value >= max)
value -= range;
while (value < min)
value += range;
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte GetByteValueFromPercentage(this double percentage)
{
if (double.IsNaN(percentage)) return 0;
percentage = percentage.Clamp(0, 1.0);
return (byte)(percentage >= 1.0 ? 255 : percentage * 256.0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double GetPercentageFromByteValue(this byte value)
=> ((double)value) / byte.MaxValue;
#endregion
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
if (value < min) return min;
if (value > max) return max;
return value;
// ReSharper restore ConvertIfStatementToReturnStatement
}
}
/// <summary>
/// Clamps the provided value to be bigger or equal min and smaller or equal max.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The lower value of the range the value is clamped to.</param>
/// <param name="max">The higher value of the range the value is clamped to.</param>
/// <returns>The clamped value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Clamp(this int value, int min, int max)
{
// ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
if (value < min) return min;
if (value > max) return max;
return value;
// ReSharper restore ConvertIfStatementToReturnStatement
}
/// <summary>
/// Enforces the provided value to be in the specified range by wrapping it around the edges if it exceeds them.
/// </summary>
/// <param name="value">The value to wrap.</param>
/// <param name="min">The lower value of the range the value is wrapped into.</param>
/// <param name="max">The higher value of the range the value is wrapped into.</param>
/// <returns>The wrapped value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Wrap(this float value, float min, float max)
{
float range = max - min;
while (value >= max)
value -= range;
while (value < min)
value += range;
return value;
}
/// <summary>
/// Converts a normalized float value in the range [0..1] to a byte [0..255].
/// </summary>
/// <param name="percentage">The normalized float value to convert.</param>
/// <returns>The byte value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte GetByteValueFromPercentage(this float percentage)
{
// ReSharper disable once ConvertIfStatementToSwitchStatement - This results in a bit more instructions
if (float.IsNaN(percentage) || (percentage <= 0)) return 0;
if (percentage >= 1.0f) return byte.MaxValue;
return (byte)(percentage * 256.0f);
}
/// <summary>
/// Converts a byte value [0..255] to a normalized float value in the range [0..1].
/// </summary>
/// <param name="value">The byte value to convert.</param>
/// <returns>The normalized float value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float GetPercentageFromByteValue(this byte value)
=> value == 255 ? 1.0f : (value / 256.0f);
#endregion
}

View File

@ -1,37 +1,43 @@
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Offers some extensions and helper-methods for <see cref="Point"/> related things.
/// </summary>
public static class PointExtensions
{
public static class PointExtensions
#region Methods
/// <summary>
/// Moves the specified <see cref="Point"/> by the specified amount.
/// </summary>
/// <param name="point">The <see cref="Point"/> to move.</param>
/// <param name="x">The x-ammount to move.</param>
/// <param name="y">The y-ammount to move.</param>
/// <returns>The new location of the point.</returns>
public static Point Translate(this Point point, float x = 0, float y = 0) => new(point.X + x, point.Y + y);
/// <summary>
/// Rotates the specified <see cref="Point"/> by the specified amuont around the specified origin.
/// </summary>
/// <param name="point">The <see cref="Point"/> to rotate.</param>
/// <param name="rotation">The rotation.</param>
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
/// <returns>The new location of the point.</returns>
public static Point Rotate(this Point point, Rotation rotation, Point origin = new())
{
#region Methods
float sin = MathF.Sin(rotation.Radians);
float cos = MathF.Cos(rotation.Radians);
/// <summary>
/// Moves the specified <see cref="Point"/> by the given amount.
/// </summary>
/// <param name="point">The <see cref="Point"/> to move.</param>
/// <param name="x">The x-ammount to move.</param>
/// <param name="y">The y-ammount to move.</param>
/// <returns>The new location of the point.</returns>
public static Point Translate(this Point point, double x = 0, double y = 0) => new Point(point.X + x, point.Y + y);
float x = point.X - origin.X;
float y = point.Y - origin.Y;
/// <summary>
/// Rotates the specified <see cref="Point"/> by the given amuont around the given origin.
/// </summary>
/// <param name="point">The <see cref="Point"/> to rotate.</param>
/// <param name="rotation">The rotation.</param>
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
/// <returns>The new location of the point.</returns>
public static Point Rotate(this Point point, Rotation rotation, Point origin = new Point())
{
double sin = Math.Sin(rotation.Radians);
double cos = Math.Cos(rotation.Radians);
x = (x * cos) - (y * sin);
y = (x * sin) + (y * cos);
point = new Point(point.X - origin.X, point.Y - origin.Y);
point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos));
return new Point(point.X + origin.X, point.Y + origin.Y); ;
}
#endregion
return new Point(x + origin.X, y + origin.Y);
}
}
#endregion
}

View File

@ -1,169 +1,177 @@
using System;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Offers some extensions and helper-methods for the work with rectangles.
/// </summary>
public static class RectangleExtensions
{
public static class RectangleExtensions
#region Methods
/// <summary>
/// Sets the <see cref="Rectangle.Location"/> of the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="location">The new location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetLocation(this Rectangle rect, Point location) => new(location, rect.Size);
/// <summary>
/// Sets the <see cref="Point.X"/> of the <see cref="Rectangle.Location"/> of the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="x">The new x-location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetX(this Rectangle rect, float x) => new(new Point(x, rect.Location.Y), rect.Size);
/// <summary>
/// Sets the <see cref="Point.Y"/> of the <see cref="Rectangle.Location"/> of the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="y">The new y-location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetY(this Rectangle rect, float y) => new(new Point(rect.Location.X, y), rect.Size);
/// <summary>
/// Sets the <see cref="Rectangle.Size"/> of the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="size">The new size of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetSize(this Rectangle rect, Size size) => new(rect.Location, size);
/// <summary>
/// Sets the <see cref="Size.Width"/> of the <see cref="Rectangle.Size"/> of the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="width">The new width of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetWidth(this Rectangle rect, float width) => new(rect.Location, new Size(width, rect.Size.Height));
/// <summary>
/// Sets the <see cref="Size.Height"/> of the <see cref="Rectangle.Size"/> of the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="height">The new height of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetHeight(this Rectangle rect, float height) => new(rect.Location, new Size(rect.Size.Width, height));
/// <summary>
/// Calculates the percentage of intersection of a rectangle.
/// </summary>
/// <param name="rect">The rectangle to calculate the intersection for.</param>
/// <param name="intersectingRect">The intersecting rectangle.</param>
/// <returns>The percentage of intersection.</returns>
public static float CalculateIntersectPercentage(this Rectangle rect, Rectangle intersectingRect)
{
#region Methods
if (rect.IsEmpty || intersectingRect.IsEmpty) return 0;
/// <summary>
/// Sets the <see cref="Rectangle.Location"/> of the given rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="location">The new location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetLocation(this Rectangle rect, Point location) => new Rectangle(location, rect.Size);
/// <summary>
/// Sets the <see cref="Point.X"/> of the <see cref="Rectangle.Location"/> of the given rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="x">The new x-location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetX(this Rectangle rect, double x) => new Rectangle(new Point(x, rect.Location.Y), rect.Size);
/// <summary>
/// Sets the <see cref="Point.Y"/> of the <see cref="Rectangle.Location"/> of the given rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="y">The new y-location of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetY(this Rectangle rect, double y) => new Rectangle(new Point(rect.Location.X, y), rect.Size);
/// <summary>
/// Sets the <see cref="Rectangle.Size"/> of the given rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="size">The new size of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetSize(this Rectangle rect, Size size) => new Rectangle(rect.Location, size);
/// <summary>
/// Sets the <see cref="Size.Width"/> of the <see cref="Rectangle.Size"/> of the given rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="width">The new width of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetWidth(this Rectangle rect, double width) => new Rectangle(rect.Location, new Size(width, rect.Size.Height));
/// <summary>
/// Sets the <see cref="Size.Height"/> of the <see cref="Rectangle.Size"/> of the given rectangle.
/// </summary>
/// <param name="rect">The rectangle to modify.</param>
/// <param name="height">The new height of the rectangle.</param>
/// <returns>The modified <see cref="Rectangle"/>.</returns>
public static Rectangle SetHeight(this Rectangle rect, double height) => new Rectangle(rect.Location, new Size(rect.Size.Width, height));
/// <summary>
/// Calculates the percentage of intersection of a rectangle.
/// </summary>
/// <param name="intersectingRect">The intersecting rectangle.</param>
/// <returns>The percentage of intersection.</returns>
public static double CalculateIntersectPercentage(this Rectangle rect, Rectangle intersectingRect)
{
if (rect.IsEmpty || intersectingRect.IsEmpty) return 0;
Rectangle intersection = rect.CalculateIntersection(intersectingRect);
return (intersection.Size.Width * intersection.Size.Height) / (rect.Size.Width * rect.Size.Height);
}
/// <summary>
/// Calculates the <see cref="Rectangle"/> representing the intersection of this <see cref="Rectangle"/> and the one provided as parameter.
/// </summary>
/// <param name="intersectingRectangle">The intersecting <see cref="Rectangle"/></param>
/// <returns>A new <see cref="Rectangle"/> representing the intersection this <see cref="Rectangle"/> and the one provided as parameter.</returns>
public static Rectangle CalculateIntersection(this Rectangle rect, Rectangle intersectingRectangle)
{
double x1 = Math.Max(rect.Location.X, intersectingRectangle.Location.X);
double x2 = Math.Min(rect.Location.X + rect.Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width);
double y1 = Math.Max(rect.Location.Y, intersectingRectangle.Location.Y);
double y2 = Math.Min(rect.Location.Y + rect.Size.Height, intersectingRectangle.Location.Y + intersectingRectangle.Size.Height);
if ((x2 >= x1) && (y2 >= y1))
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
return new Rectangle();
}
/// <summary>
/// Determines if the specified <see cref="Point"/> is contained within this <see cref="Rectangle"/>.
/// </summary>
/// <param name="point">The <see cref="Point"/> to test.</param>
/// <returns><c>true</c> if the rectangle contains the given point; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, Point point) => rect.Contains(point.X, point.Y);
/// <summary>
/// Determines if the specified location is contained within this <see cref="Rectangle"/>.
/// </summary>
/// <param name="x">The X-location to test.</param>
/// <param name="y">The Y-location to test.</param>
/// <returns><c>true</c> if the rectangle contains the given coordinates; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, double x, double y) => (rect.Location.X <= x) && (x < (rect.Location.X + rect.Size.Width))
&& (rect.Location.Y <= y) && (y < (rect.Location.Y + rect.Size.Height));
/// <summary>
/// Determines if the specified <see cref="Rectangle"/> is contained within this <see cref="Rectangle"/>.
/// </summary>
/// <param name="rect">The <see cref="Rectangle"/> to test.</param>
/// <returns><c>true</c> if the rectangle contains the given rect; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, Rectangle rect2) => (rect.Location.X <= rect2.Location.X) && ((rect2.Location.X + rect2.Size.Width) <= (rect.Location.X + rect.Size.Width))
&& (rect.Location.Y <= rect2.Location.Y) && ((rect2.Location.Y + rect2.Size.Height) <= (rect.Location.Y + rect.Size.Height));
/// <summary>
/// Moves the specified <see cref="Rectangle"/> by the given amount.
/// </summary>
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
/// <param name="point">The amount to move.</param>
/// <returns>The moved rectangle.</returns>
public static Rectangle Translate(this Rectangle rect, Point point) => rect.Translate(point.X, point.Y);
/// <summary>
/// Moves the specified <see cref="Rectangle"/> by the given amount.
/// </summary>
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
/// <param name="x">The x-ammount to move.</param>
/// <param name="y">The y-ammount to move.</param>
/// <returns>The moved rectangle.</returns>
public static Rectangle Translate(this Rectangle rect, double x = 0, double y = 0) => new Rectangle(rect.Location.Translate(x, y), rect.Size);
/// <summary>
/// Rotates the specified <see cref="Rectangle"/> by the given amuont around the given origin.
/// </summary>
/// <remarks>
/// The returned array of <see cref="Point"/> is filled with the new locations of the rectangle clockwise starting from the top left:
/// [0] = top left
/// [1] = top right
/// [2] = bottom right
/// [3] = bottom left
/// </remarks>
/// <param name="rect">The <see cref="Rectangle"/> to rotate.</param>
/// <param name="rotation">The rotation.</param>
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
/// <returns>A array of <see cref="Point"/> containing the new locations of the corners of the original rectangle.</returns>
public static Point[] Rotate(this Rectangle rect, Rotation rotation, Point origin = new Point())
{
Point[] points = {
rect.Location, // top left
new Point(rect.Location.X + rect.Size.Width, rect.Location.Y), // top right
new Point(rect.Location.X + rect.Size.Width, rect.Location.Y + rect.Size.Height), // bottom right
new Point(rect.Location.X, rect.Location.Y + rect.Size.Height), // bottom right
};
double sin = Math.Sin(rotation.Radians);
double cos = Math.Cos(rotation.Radians);
for (int i = 0; i < points.Length; i++)
{
Point point = points[i];
point = new Point(point.X - origin.X, point.Y - origin.Y);
point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos));
points[i] = new Point(point.X + origin.X, point.Y + origin.Y);
}
return points;
}
#endregion
Rectangle intersection = rect.CalculateIntersection(intersectingRect);
return (intersection.Size.Width * intersection.Size.Height) / (rect.Size.Width * rect.Size.Height);
}
}
/// <summary>
/// Calculates the <see cref="Rectangle"/> representing the intersection of this <see cref="Rectangle"/> and the one provided as parameter.
/// </summary>
/// <param name="rect">The rectangle to calculate the intersection for.</param>
/// <param name="intersectingRectangle">The intersecting <see cref="Rectangle"/>.</param>
/// <returns>A new <see cref="Rectangle"/> representing the intersection this <see cref="Rectangle"/> and the one provided as parameter.</returns>
public static Rectangle CalculateIntersection(this Rectangle rect, Rectangle intersectingRectangle)
{
float x1 = Math.Max(rect.Location.X, intersectingRectangle.Location.X);
float x2 = Math.Min(rect.Location.X + rect.Size.Width, intersectingRectangle.Location.X + intersectingRectangle.Size.Width);
float y1 = Math.Max(rect.Location.Y, intersectingRectangle.Location.Y);
float y2 = Math.Min(rect.Location.Y + rect.Size.Height, intersectingRectangle.Location.Y + intersectingRectangle.Size.Height);
if ((x2 >= x1) && (y2 >= y1))
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
return new Rectangle();
}
/// <summary>
/// Determines if the specified <see cref="Point"/> is contained within this <see cref="Rectangle"/>.
/// </summary>
/// <param name="rect">The containing rectangle.</param>
/// <param name="point">The <see cref="Point"/> to test.</param>
/// <returns><c>true</c> if the rectangle contains the specified point; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, Point point) => rect.Contains(point.X, point.Y);
/// <summary>
/// Determines if the specified location is contained within this <see cref="Rectangle"/>.
/// </summary>
/// <param name="rect">The containing rectangle.</param>
/// <param name="x">The X-location to test.</param>
/// <param name="y">The Y-location to test.</param>
/// <returns><c>true</c> if the rectangle contains the specified coordinates; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, float x, float y) => (rect.Location.X <= x) && (x < (rect.Location.X + rect.Size.Width))
&& (rect.Location.Y <= y) && (y < (rect.Location.Y + rect.Size.Height));
/// <summary>
/// Determines if the specified <see cref="Rectangle"/> is contained within this <see cref="Rectangle"/>.
/// </summary>
/// <param name="rect">The containing rectangle.</param>
/// <param name="rect2">The <see cref="Rectangle"/> to test.</param>
/// <returns><c>true</c> if the rectangle contains the specified rect; otherwise <c>false</c>.</returns>
public static bool Contains(this Rectangle rect, Rectangle rect2) => (rect.Location.X <= rect2.Location.X) && ((rect2.Location.X + rect2.Size.Width) <= (rect.Location.X + rect.Size.Width))
&& (rect.Location.Y <= rect2.Location.Y) && ((rect2.Location.Y + rect2.Size.Height) <= (rect.Location.Y + rect.Size.Height));
/// <summary>
/// Moves the specified <see cref="Rectangle"/> by the specified amount.
/// </summary>
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
/// <param name="point">The amount to move.</param>
/// <returns>The moved rectangle.</returns>
public static Rectangle Translate(this Rectangle rect, Point point) => rect.Translate(point.X, point.Y);
/// <summary>
/// Moves the specified <see cref="Rectangle"/> by the specified amount.
/// </summary>
/// <param name="rect">The <see cref="Rectangle"/> to move.</param>
/// <param name="x">The x-ammount to move.</param>
/// <param name="y">The y-ammount to move.</param>
/// <returns>The moved rectangle.</returns>
public static Rectangle Translate(this Rectangle rect, float x = 0, float y = 0) => new(rect.Location.Translate(x, y), rect.Size);
/// <summary>
/// Rotates the specified <see cref="Rectangle"/> by the specified amuont around the specified origin.
/// </summary>
/// <remarks>
/// The returned array of <see cref="Point"/> is filled with the new locations of the rectangle clockwise starting from the top left:
/// [0] = top left
/// [1] = top right
/// [2] = bottom right
/// [3] = bottom left
/// </remarks>
/// <param name="rect">The <see cref="Rectangle"/> to rotate.</param>
/// <param name="rotation">The rotation.</param>
/// <param name="origin">The origin to rotate around. [0,0] if not set.</param>
/// <returns>A array of <see cref="Point"/> containing the new locations of the corners of the original rectangle.</returns>
public static Point[] Rotate(this Rectangle rect, Rotation rotation, Point origin = new())
{
Point[] points =
[
rect.Location, // top left
new(rect.Location.X + rect.Size.Width, rect.Location.Y), // top right
new(rect.Location.X + rect.Size.Width, rect.Location.Y + rect.Size.Height), // bottom right
new(rect.Location.X, rect.Location.Y + rect.Size.Height), // bottom right
];
float sin = MathF.Sin(rotation.Radians);
float cos = MathF.Cos(rotation.Radians);
for (int i = 0; i < points.Length; i++)
{
Point point = points[i];
point = new Point(point.X - origin.X, point.Y - origin.Y);
point = new Point((point.X * cos) - (point.Y * sin), (point.X * sin) + (point.Y * cos));
points[i] = new Point(point.X + origin.X, point.Y + origin.Y);
}
return points;
}
#endregion
}

View File

@ -0,0 +1,6 @@
namespace RGB.NET.Core;
public static class ReferenceCountingExtension
{
public static bool HasActiveReferences(this IReferenceCounting target) => target.ActiveReferenceCount > 0;
}

View File

@ -0,0 +1,84 @@
// ReSharper disable UnusedMember.Global
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core;
/// <summary>
/// Offers some extensions and helper-methods for the work with the surface.
/// </summary>
public static class SurfaceExtensions
{
#region Methods
/// <summary>
/// Initializes the specifiec device provider and attaches all devices.
/// </summary>
/// <param name="surface">The surface to attach the devices to.</param>
/// <param name="deviceProvider">The device provider to load.</param>
/// <param name="loadFilter"><see cref="RGBDeviceType"/>-flags to filter the devices to load.</param>
/// <param name="throwExceptions">Specifies if exceptions should be thrown or silently be ignored.</param>
public static void Load(this RGBSurface surface, IRGBDeviceProvider deviceProvider, RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
{
if (!deviceProvider.IsInitialized)
deviceProvider.Initialize(loadFilter, throwExceptions);
surface.Attach(deviceProvider.Devices);
}
/// <summary>
/// Attaches the specified devices to the surface.
/// </summary>
/// <param name="surface">The surface the devices are attached to.</param>
/// <param name="devices">The devices to attach.</param>
public static void Attach(this RGBSurface surface, IEnumerable<IRGBDevice> devices)
{
foreach (IRGBDevice device in devices)
surface.Attach(device);
}
/// <summary>
/// Detaches the specified devices from the surface.
/// </summary>
/// <param name="surface">The surface the devices are detached from.</param>
/// <param name="devices">The devices to detach.</param>
public static void Detach(this RGBSurface surface, IEnumerable<IRGBDevice> devices)
{
foreach (IRGBDevice device in devices)
surface.Detach(device);
}
/// <summary>
/// Gets all devices of a specific type.
/// </summary>
/// <typeparam name="T">The type of devices to get.</typeparam>
/// <returns>A collection of devices with the specified type.</returns>
public static IEnumerable<T> GetDevices<T>(this RGBSurface surface)
where T : class
=> surface.Devices.Where(x => x is T).Cast<T>();
/// <summary>
/// Gets all devices of the specified <see cref="RGBDeviceType"/>.
/// </summary>
/// <param name="surface">The surface to get the devices from.</param>
/// <param name="deviceType">The <see cref="RGBDeviceType"/> of the devices to get.</param>
/// <returns>A collection of devices matching the specified <see cref="RGBDeviceType"/>.</returns>
public static IEnumerable<IRGBDevice> GetDevices(this RGBSurface surface, RGBDeviceType deviceType)
=> surface.Devices.Where(d => deviceType.HasFlag(d.DeviceInfo.DeviceType));
/// <summary>
/// Automatically aligns all devices to prevent overlaps.
/// </summary>
public static void AlignDevices(this RGBSurface surface)
{
float posX = 0;
foreach (IRGBDevice device in surface.Devices)
{
device.Location += new Point(posX, 0);
posX += device.ActualSize.Width + 1;
}
}
#endregion
}

View File

@ -1,51 +1,75 @@
using System.Collections.Generic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc cref="AbstractDecoratable{T}" />
/// <inheritdoc cref="ILedGroup" />
/// <summary>
/// Represents a generic <see cref="T:RGB.NET.Core.AbstractLedGroup" />.
/// </summary>
public abstract class AbstractLedGroup : AbstractDecoratable<ILedGroupDecorator>, ILedGroup
{
/// <inheritdoc cref="AbstractDecoratable{T}" />
/// <inheritdoc cref="ILedGroup" />
#region Properties & Fields
RGBSurface? ILedGroup.Surface { get; set; }
/// <inheritdoc cref="ILedGroup.Surface" />
public RGBSurface? Surface => ((ILedGroup)this).Surface;
/// <inheritdoc />
public IBrush? Brush { get; set; }
/// <inheritdoc />
public int ZIndex { get; set; } = 0;
#endregion
#region Constructors
/// <summary>
/// Represents a generic <see cref="T:RGB.NET.Core.AbstractLedGroup" />.
/// Initializes a new instance of the <see cref="AbstractLedGroup"/> class.
/// </summary>
public abstract class AbstractLedGroup : AbstractDecoratable<ILedGroupDecorator>, ILedGroup
protected AbstractLedGroup(RGBSurface? attachTo)
{
#region Properties & Fields
/// <inheritdoc />
public IBrush Brush { get; set; }
/// <inheritdoc />
public int ZIndex { get; set; } = 0;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AbstractLedGroup"/> class.
/// </summary>
/// <param name="autoAttach">Specifies whether this <see cref="AbstractLedGroup"/> should be automatically attached or not.</param>
protected AbstractLedGroup(bool autoAttach)
{
if (autoAttach)
RGBSurface.Instance.AttachLedGroup(this);
}
#endregion
#region Methods
/// <inheritdoc />
public abstract IList<Led> GetLeds();
/// <inheritdoc />
public virtual void OnAttach()
{ }
/// <inheritdoc />
public virtual void OnDetach()
{ }
#endregion
attachTo?.Attach(this);
}
}
#endregion
#region Methods
/// <summary>
/// Gets a enumerable containing all leds in this group.
/// </summary>
/// <returns>A enumerable containing all leds of this group.</returns>
protected abstract IEnumerable<Led> GetLeds();
/// <inheritdoc />
public virtual void OnAttach() { }
/// <inheritdoc />
public virtual void OnDetach() { }
/// <inheritdoc />
public virtual IList<Led> ToList() => GetLeds().ToList();
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <inheritdoc />
public IEnumerator<Led> GetEnumerator() => GetLeds().GetEnumerator();
/// <inheritdoc />
IDisposable? ILedGroup.ToListUnsafe(out IList<Led> leds) => ToListUnsafe(out leds);
protected virtual IDisposable? ToListUnsafe(out IList<Led> leds)
{
leds = ToList();
return null;
}
#endregion
}

View File

@ -1,40 +1,51 @@
// ReSharper disable UnusedMemberInSuper.Global
// ReSharper disable UnusedMember.Global
using System;
using System.Collections.Generic;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <summary>
/// Represents a generic ledgroup.
/// </summary>
public interface ILedGroup : IDecoratable<ILedGroupDecorator>, IEnumerable<Led>
{
/// <inheritdoc />
/// <summary>
/// Represents a generic ledgroup.
/// Gets the surface this group is attached to or <c>null</c> if it is not attached to any surface.
/// </summary>
public interface ILedGroup : IDecoratable<ILedGroupDecorator>
{
/// <summary>
/// Gets or sets the <see cref="IBrush"/> which should be drawn over this <see cref="ILedGroup"/>.
/// </summary>
IBrush Brush { get; set; }
RGBSurface? Surface { get; internal set; }
/// <summary>
/// Gets or sets the z-index of this <see cref="ILedGroup"/> to allow ordering them before drawing. (lowest first) (default: 0)
/// </summary>
int ZIndex { get; set; }
/// <summary>
/// Gets a bool indicating if the group is attached to a surface.
/// </summary>
bool IsAttached => Surface != null;
/// <summary>
/// Gets a list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.
/// </summary>
/// <returns>The list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.</returns>
IList<Led> GetLeds();
/// <summary>
/// Gets or sets the <see cref="IBrush"/> which should be drawn over this <see cref="ILedGroup"/>.
/// </summary>
IBrush? Brush { get; set; }
/// <summary>
/// Called when the <see cref="ILedGroup"/> is attached to the <see cref="RGBSurface"/>.
/// </summary>
void OnAttach();
/// <summary>
/// Gets or sets the z-index of this <see cref="ILedGroup"/> to allow ordering them before drawing. (lowest first) (default: 0)
/// </summary>
int ZIndex { get; set; }
/// <summary>
/// Called when the <see cref="ILedGroup"/> is detached from the <see cref="RGBSurface"/>.
/// </summary>
void OnDetach();
}
}
/// <summary>
/// Called when the <see cref="ILedGroup"/> is attached to the <see cref="RGBSurface"/>.
/// </summary>
void OnAttach();
/// <summary>
/// Called when the <see cref="ILedGroup"/> is detached from the <see cref="RGBSurface"/>.
/// </summary>
void OnDetach();
/// <summary>
/// Returns a list containing all <see cref="Led"/> in this group.
/// </summary>
/// <returns>A list containing all <see cref="Led"/> in this group.</returns>
IList<Led> ToList();
internal IDisposable? ToListUnsafe(out IList<Led> leds);
}

View File

@ -0,0 +1,57 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
namespace RGB.NET.Core;
/// <summary>
/// Offers some extensions and helper-methods for <see cref="ILedGroup"/> related things.
/// </summary>
public static class LedGroupExtension
{
/// <summary>
/// Converts the specified <see cref="ILedGroup" /> to a <see cref="ListLedGroup" />.
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup" /> to convert.</param>
/// <returns>The converted <see cref="ListLedGroup" />.</returns>
public static ListLedGroup ToListLedGroup(this ILedGroup ledGroup)
{
// ReSharper disable once InvertIf
if (ledGroup is not ListLedGroup listLedGroup)
{
if (ledGroup.IsAttached)
ledGroup.Detach();
listLedGroup = new ListLedGroup(ledGroup.Surface, ledGroup) { Brush = ledGroup.Brush, ZIndex = ledGroup.ZIndex };
}
return listLedGroup;
}
/// <summary>
/// Returns a new <see cref="ListLedGroup" /> which contains all <see cref="Led"/> from the specified <see cref="ILedGroup"/> excluding the specified ones.
/// </summary>
/// <param name="ledGroup">The base <see cref="ILedGroup"/>.</param>
/// <param name="ledIds">The <see cref="Led"/> to exclude.</param>
/// <returns>The new <see cref="ListLedGroup" />.</returns>
public static ListLedGroup Exclude(this ILedGroup ledGroup, params Led[] ledIds)
{
ListLedGroup listLedGroup = ledGroup.ToListLedGroup();
foreach (Led led in ledIds)
listLedGroup.RemoveLed(led);
return listLedGroup;
}
// ReSharper disable once UnusedMethodReturnValue.Global
/// <summary>
/// Attaches the specified <see cref="ILedGroup"/> to the <see cref="RGBSurface"/>.
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
/// <param name="surface">The <see cref="RGBSurface"/> to attach this group to.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be attached; otherwise, <c>false</c>.</returns>
public static bool Attach(this ILedGroup ledGroup, RGBSurface surface) => surface.Attach(ledGroup);
/// <summary>
/// Detaches the specified <see cref="ILedGroup"/> from the <see cref="RGBSurface"/>.
/// </summary>
/// <param name="ledGroup">The <see cref="ILedGroup"/> to attach.</param>
/// <returns><c>true</c> if the <see cref="ILedGroup"/> could be detached; otherwise, <c>false</c>.</returns>
public static bool Detach(this ILedGroup ledGroup) => ledGroup.Surface?.Detach(ledGroup) ?? false;
}

View File

@ -0,0 +1,158 @@
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
using System.Collections.Generic;
using System.Threading;
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents a ledgroup containing arbitrary <see cref="T:RGB.NET.Core.Led" />.
/// </summary>
public sealed class ListLedGroup : AbstractLedGroup
{
#region Properties & Fields
private readonly ActionDisposable _unlockDisposable;
/// <summary>
/// Gets the list containing the <see cref="Led"/> of this <see cref="ListLedGroup"/>.
/// </summary>
private readonly IList<Led> _groupLeds = [];
#endregion
#region Constructors
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.ListLedGroup" /> class.
/// </summary>
/// <param name="surface">Specifies the surface to attach this group to or <c>null</c> if the group should not be attached on creation.</param>
public ListLedGroup(RGBSurface? surface)
: base(surface)
{
_unlockDisposable = new ActionDisposable(Unlock);
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.ListLedGroup" /> class.
/// </summary>
/// <param name="surface">Specifies the surface to attach this group to or <c>null</c> if the group should not be attached on creation.</param>
/// <param name="leds">The initial <see cref="T:RGB.NET.Core.Led" /> of this <see cref="T:RGB.NET.Groups.ListLedGroup" />.</param>
public ListLedGroup(RGBSurface? surface, IEnumerable<Led> leds)
: base(surface)
{
_unlockDisposable = new ActionDisposable(Unlock);
AddLeds(leds);
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:RGB.NET.Groups.ListLedGroup" /> class.
/// </summary>
/// <param name="surface">Specifies the surface to attach this group to or <c>null</c> if the group should not be attached on creation.</param>
/// <param name="leds">The initial <see cref="T:RGB.NET.Core.Led" /> of this <see cref="T:RGB.NET.Groups.ListLedGroup" />.</param>
public ListLedGroup(RGBSurface? surface, params Led[] leds)
: base(surface)
{
_unlockDisposable = new ActionDisposable(Unlock);
AddLeds(leds);
}
#endregion
#region Methods
/// <summary>
/// Adds the specified LED(s) to this <see cref="ListLedGroup"/>.
/// </summary>
/// <param name="leds">The LED(s) to add.</param>
public void AddLed(params Led[] leds) => AddLeds(leds);
/// <summary>
/// Adds the specified <see cref="Led"/> to this <see cref="ListLedGroup"/>.
/// </summary>
/// <param name="leds">The <see cref="Led"/> to add.</param>
public void AddLeds(IEnumerable<Led> leds)
{
lock (_groupLeds)
foreach (Led led in leds)
if (!ContainsLed(led))
_groupLeds.Add(led);
}
/// <summary>
/// Removes the specified LED(s) from this <see cref="ListLedGroup"/>.
/// </summary>
/// <param name="leds">The LED(s) to remove.</param>
public void RemoveLed(params Led[] leds) => RemoveLeds(leds);
/// <summary>
/// Removes the specified <see cref="Led"/> from this <see cref="ListLedGroup"/>.
/// </summary>
/// <param name="leds">The <see cref="Led"/> to remove.</param>
public void RemoveLeds(IEnumerable<Led> leds)
{
lock (_groupLeds)
foreach (Led led in leds)
_groupLeds.Remove(led);
}
/// <summary>
/// Checks if a specified LED is contained by this ledgroup.
/// </summary>
/// <param name="led">The LED which should be checked.</param>
/// <returns><c>true</c> if the LED is contained by this ledgroup; otherwise, <c>false</c>.</returns>
public bool ContainsLed(Led led)
{
lock (_groupLeds)
return _groupLeds.Contains(led);
}
/// <summary>
/// Merges the <see cref="Led"/> from the specified ledgroup in this ledgroup.
/// </summary>
/// <param name="groupToMerge">The ledgroup to merge.</param>
public void MergeLeds(ILedGroup groupToMerge)
{
lock (_groupLeds)
foreach (Led led in groupToMerge)
if (!_groupLeds.Contains(led))
_groupLeds.Add(led);
}
/// <inheritdoc />
/// <summary>
/// Gets a list containing the <see cref="T:RGB.NET.Core.Led" /> from this group.
/// </summary>
/// <returns>The list containing the <see cref="T:RGB.NET.Core.Led" />.</returns>
protected override IEnumerable<Led> GetLeds() => ToList();
/// <inheritdoc />
/// <summary>
/// Gets a list containing the <see cref="T:RGB.NET.Core.Led" /> from this group.
/// </summary>
/// <returns>The list containing the <see cref="T:RGB.NET.Core.Led" />.</returns>
public override IList<Led> ToList()
{
lock (_groupLeds)
return [.._groupLeds];
}
protected override IDisposable ToListUnsafe(out IList<Led> leds)
{
Monitor.Enter(_groupLeds);
leds = _groupLeds;
return _unlockDisposable;
}
private void Unlock() => Monitor.Exit(_groupLeds);
#endregion
}

View File

@ -1,60 +1,61 @@
namespace RGB.NET.Core
using System;
namespace RGB.NET.Core;
/// <summary>
/// Contains helper methods for converting things.
/// </summary>
public static class ConversionHelper
{
#region Methods
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535
/// <summary>
/// Contains helper methods for converting things.
/// Converts an array of bytes to a HEX-representation.
/// </summary>
public static class ConversionHelper
/// <param name="bytes">The array of bytes.</param>
/// <returns>The HEX-representation of the provided bytes.</returns>
public static string ToHex(params byte[] bytes)
{
#region Methods
char[] c = new char[bytes.Length * 2];
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535
/// <summary>
/// Converts an array of bytes to a HEX-representation.
/// </summary>
/// <param name="bytes">The array of bytes.</param>
/// <returns>The HEX-representation of the provided bytes.</returns>
public static string ToHex(params byte[] bytes)
for (int bx = 0, cx = 0; bx < bytes.Length; ++bx, ++cx)
{
char[] c = new char[bytes.Length * 2];
byte b = ((byte)(bytes[bx] >> 4));
c[cx] = (char)(b > 9 ? b + 0x37 : b + 0x30);
for (int bx = 0, cx = 0; bx < bytes.Length; ++bx, ++cx)
{
byte b = ((byte)(bytes[bx] >> 4));
c[cx] = (char)(b > 9 ? b + 0x37: b + 0x30);
b = ((byte)(bytes[bx] & 0x0F));
c[++cx] = (char)(b > 9 ? b + 0x37: b + 0x30);
}
return new string(c);
b = ((byte)(bytes[bx] & 0x0F));
c[++cx] = (char)(b > 9 ? b + 0x37 : b + 0x30);
}
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535
/// <summary>
/// Converts the HEX-representation of a byte array to that array.
/// </summary>
/// <param name="hexString">The HEX-string to convert.</param>
/// <returns>The correspondending byte array.</returns>
public static byte[] HexToBytes(string hexString)
{
if ((hexString.Length == 0) || ((hexString.Length % 2) != 0))
return new byte[0];
byte[] buffer = new byte[hexString.Length / 2];
for (int bx = 0, sx = 0; bx < buffer.Length; ++bx, ++sx)
{
// Convert first half of byte
char c = hexString[sx];
buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? ((c - 'a') + 10) : ((c - 'A') + 10)) : (c - '0')) << 4);
// Convert second half of byte
c = hexString[++sx];
buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? ((c - 'a') + 10) : ((c - 'A') + 10)) : (c - '0'));
}
return buffer;
}
#endregion
return new string(c);
}
}
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535
/// <summary>
/// Converts the HEX-representation of a byte array to that array.
/// </summary>
/// <param name="hexString">The HEX-string to convert.</param>
/// <returns>The correspondending byte array.</returns>
public static byte[] HexToBytes(ReadOnlySpan<char> hexString)
{
if ((hexString.Length == 0) || ((hexString.Length % 2) != 0))
return [];
byte[] buffer = new byte[hexString.Length / 2];
for (int bx = 0, sx = 0; bx < buffer.Length; ++bx, ++sx)
{
// Convert first half of byte
char c = hexString[sx];
buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? ((c - 'a') + 10) : ((c - 'A') + 10)) : (c - '0')) << 4);
// Convert second half of byte
c = hexString[++sx];
buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? ((c - 'a') + 10) : ((c - 'A') + 10)) : (c - '0'));
}
return buffer;
}
#endregion
}

View File

@ -1,44 +0,0 @@
using System;
using System.Globalization;
using System.Runtime.InteropServices;
namespace RGB.NET.Core
{
/// <summary>
/// Offers some helper-methods for culture related things.
/// </summary>
public static class CultureHelper
{
#region DLLImports
[DllImport("user32.dll")]
private static extern IntPtr GetKeyboardLayout(uint thread);
#endregion
#region Constructors
#endregion
#region Methods
/// <summary>
/// Gets the current keyboard-layout from the OS.
/// </summary>
/// <returns>The current keyboard-layout</returns>
public static CultureInfo GetCurrentCulture()
{
try
{
int keyboardLayout = GetKeyboardLayout(0).ToInt32() & 0xFFFF;
return new CultureInfo(keyboardLayout);
}
catch
{
return new CultureInfo(1033); // en-US on error.
}
}
#endregion
}
}

View File

@ -0,0 +1,26 @@
using System.Reflection;
using System.Runtime.CompilerServices;
namespace RGB.NET.Core;
/// <summary>
/// Offsers some helper methods for device creation.
/// </summary>
public static class DeviceHelper
{
#region Methods
/// <summary>
/// Creates a unique device name from a manufacturer and model name.
/// </summary>
/// <remarks>
/// The id is made unique based on the assembly calling this method.
/// </remarks>
/// <param name="manufacturer">The manufacturer of the device.</param>
/// <param name="model">The model of the device.</param>
/// <returns>The unique identifier for this device.</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
public static string CreateDeviceName(string manufacturer, string model) => IdGenerator.MakeUnique(Assembly.GetCallingAssembly(), $"{manufacturer} {model}");
#endregion
}

View File

@ -1,83 +0,0 @@
using System;
using System.IO;
using System.Reflection;
namespace RGB.NET.Core
{
/// <summary>
/// Offers some helper-methods for file-path related things.
/// </summary>
public static class PathHelper
{
#region Events
/// <summary>
/// Occurs when a path is resolving.
/// </summary>
public static event EventHandler<ResolvePathEventArgs> ResolvingAbsolutePath;
#endregion
#region Methods
/// <summary>
/// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
/// </summary>
/// <param name="relativePath">The relative part of the path to convert.</param>
/// <returns>The absolute path.</returns>
public static string GetAbsolutePath(string relativePath) => GetAbsolutePath((object)null, relativePath);
/// <summary>
/// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
/// </summary>
/// <param name="relativePath">The relative part of the path to convert.</param>
/// <param name="fileName">The file name of the path to convert.</param>
/// <returns>The absolute path.</returns>
public static string GetAbsolutePath(string relativePath, string fileName) => GetAbsolutePath(null, relativePath, fileName);
/// <summary>
/// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
/// </summary>
/// <param name="sender">The requester of this path. (Used for better control when using the event to override this behavior.)</param>
/// <param name="relativePath">The relative path to convert.</param>
/// <param name="fileName">The file name of the path to convert.</param>
/// <returns>The absolute path.</returns>
public static string GetAbsolutePath(object sender, string relativePath, string fileName)
{
string relativePart = Path.Combine(relativePath, fileName);
string assemblyLocation = Assembly.GetEntryAssembly()?.Location;
if (assemblyLocation == null) return relativePart;
string directoryName = Path.GetDirectoryName(assemblyLocation);
string path = directoryName == null ? null : Path.Combine(directoryName, relativePart);
ResolvePathEventArgs args = new ResolvePathEventArgs(relativePath, fileName, path);
ResolvingAbsolutePath?.Invoke(sender, args);
return args.FinalPath;
}
/// <summary>
/// Returns an absolute path created from an relative path relatvie to the location of the executung assembly.
/// </summary>
/// <param name="sender">The requester of this path. (Used for better control when using the event to override this behavior.)</param>
/// <param name="relativePath">The relative path to convert.</param>
/// <returns>The absolute path.</returns>
public static string GetAbsolutePath(object sender, string relativePath)
{
string assemblyLocation = Assembly.GetEntryAssembly()?.Location;
if (assemblyLocation == null) return relativePath;
string directoryName = Path.GetDirectoryName(assemblyLocation);
string path = directoryName == null ? null : Path.Combine(directoryName, relativePath);
ResolvePathEventArgs args = new ResolvePathEventArgs(relativePath, path);
ResolvingAbsolutePath?.Invoke(sender, args);
return args.FinalPath;
}
#endregion
}
}

View File

@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace RGB.NET.Core;
/// <summary>
/// Offers some helper methods for timed operations.
/// </summary>
public static class TimerHelper
{
#region DLL-Imports
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
private static extern void TimeBeginPeriod(int t);
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
private static extern void TimeEndPeriod(int t);
#endregion
#region Properties & Fields
private static readonly Lock HIGH_RESOLUTION_TIMER_LOCK = new();
private static bool _areHighResolutionTimersEnabled = false;
private static bool _useHighResolutionTimers = true;
/// <summary>
/// Gets or sets if High Resolution Timers should be used.
/// </summary>
public static bool UseHighResolutionTimers
{
get => _useHighResolutionTimers;
set
{
lock (HIGH_RESOLUTION_TIMER_LOCK)
{
_useHighResolutionTimers = value;
CheckHighResolutionTimerUsage();
}
}
}
// ReSharper disable once InconsistentNaming
private static readonly HashSet<HighResolutionTimerDisposable> _timerLeases = [];
#endregion
#region Methods
/// <summary>
/// Executes the provided action and blocks if needed until the the <see param="targetExecuteTime"/> has passed.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <param name="targetExecuteTime">The time in ms this method should block. default: 0</param>
/// <returns>The time in ms spent executing the <see param="action"/>.</returns>
public static double Execute(Action action, double targetExecuteTime = 0)
{
long preUpdateTicks = Stopwatch.GetTimestamp();
action();
double updateTime = GetElapsedTime(preUpdateTicks);
if (targetExecuteTime > 0)
{
int sleep = (int)(targetExecuteTime - updateTime);
if (sleep > 0)
Thread.Sleep(sleep);
}
return updateTime;
}
/// <summary>
/// Calculates the elapsed time in ms from the provided timestamp until now.
/// </summary>
/// <param name="initialTimestamp">The initial timestamp to calculate the time from.</param>
/// <returns>The elapsed time in ms.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double GetElapsedTime(long initialTimestamp) => ((Stopwatch.GetTimestamp() - initialTimestamp) / (Stopwatch.Frequency / 1000.0));
/// <summary>
/// Requests to use to use High Resolution Timers if enabled.
/// IMPORTANT: Always dispose the returned disposable if High Resolution Timers are no longer needed for the caller.
/// </summary>
/// <returns>A disposable to remove the request.</returns>
public static IDisposable RequestHighResolutionTimer()
{
HighResolutionTimerDisposable timerLease = new();
lock (HIGH_RESOLUTION_TIMER_LOCK)
{
_timerLeases.Add(timerLease);
CheckHighResolutionTimerUsage();
}
return timerLease;
}
private static void CheckHighResolutionTimerUsage()
{
if (UseHighResolutionTimers && (_timerLeases.Count > 0))
EnableHighResolutionTimers();
else
DisableHighResolutionTimers();
}
private static void EnableHighResolutionTimers()
{
lock (HIGH_RESOLUTION_TIMER_LOCK)
{
if (_areHighResolutionTimersEnabled) return;
// DarthAffe 06.05.2022: Linux should use 1ms timers by default
if (OperatingSystem.IsWindows())
TimeBeginPeriod(1);
_areHighResolutionTimersEnabled = true;
}
}
private static void DisableHighResolutionTimers()
{
lock (HIGH_RESOLUTION_TIMER_LOCK)
{
if (!_areHighResolutionTimersEnabled) return;
if (OperatingSystem.IsWindows())
TimeEndPeriod(1);
_areHighResolutionTimersEnabled = false;
}
}
/// <summary>
/// Disposes all open High Resolution Timer Requests.
/// This should be called once when exiting the application to make sure nothing remains open and the application correctly unregisters itself on OS level.
/// Shouldn't be needed if everything is disposed, but better safe then sorry.
/// </summary>
public static void DisposeAllHighResolutionTimerRequests()
{
List<HighResolutionTimerDisposable> timerLeases = [.._timerLeases];
foreach (HighResolutionTimerDisposable timer in timerLeases)
timer.Dispose();
}
#endregion
private class HighResolutionTimerDisposable : IDisposable
{
#region Properties & Fields
private bool _isDisposed = false;
#endregion
#region Methods
public void Dispose()
{
if (_isDisposed) return;
_isDisposed = true;
lock (HIGH_RESOLUTION_TIMER_LOCK)
{
_timerLeases.Remove(this);
CheckHighResolutionTimerUsage();
}
}
#endregion
}
}

View File

@ -0,0 +1,73 @@
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace RGB.NET.Core;
/// <summary>
/// Offers some methods to create and handle unique identifiers.
/// </summary>
public static class IdGenerator
{
#region Properties & Fields
// ReSharper disable InconsistentNaming
private static readonly HashSet<string> _registeredIds = [];
private static readonly Dictionary<Assembly, Dictionary<string, string>> _idMappings = [];
private static readonly Dictionary<Assembly, Dictionary<string, int>> _counter = [];
// ReSharper restore InconsistentNaming
#endregion
#region Methods
/// <summary>
/// Makes the specified id unique based on the calling assembly by adding a counter if needed.
/// </summary>
/// <param name="id">The id to make unique.</param>
/// <returns>The unique id.</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
public static string MakeUnique(string id) => MakeUnique(Assembly.GetCallingAssembly(), id);
internal static string MakeUnique(Assembly callingAssembly, string id)
{
if (!_idMappings.TryGetValue(callingAssembly, out Dictionary<string, string>? idMapping))
{
_idMappings.Add(callingAssembly, idMapping = []);
_counter.Add(callingAssembly, []);
}
Dictionary<string, int> counterMapping = _counter[callingAssembly];
if (!idMapping.TryGetValue(id, out string? mappedId))
{
mappedId = id;
int mappingCounter = 1;
while (_registeredIds.Contains(mappedId))
mappedId = $"{id} ({++mappingCounter})";
_registeredIds.Add(mappedId);
idMapping.Add(id, mappedId);
}
counterMapping.TryAdd(mappedId, 0);
int counter = ++counterMapping[mappedId];
return counter <= 1 ? mappedId : $"{mappedId} ({counter})";
}
/// <summary>
/// Resets the counter used to create unique ids.
/// All previous generated ids are not garantueed to stay unique if this is called!
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ResetCounter() => ResetCounter(Assembly.GetCallingAssembly());
internal static void ResetCounter(Assembly callingAssembly)
{
if (_counter.TryGetValue(callingAssembly, out Dictionary<string, int>? counter))
counter.Clear();
}
#endregion
}

View File

@ -1,310 +1,175 @@
// ReSharper disable MemberCanBePrivate.Global
using System;
using System.ComponentModel;
using System.Diagnostics;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents a single LED of a RGB-device.
/// </summary>
[DebuggerDisplay("{Id} {Color}")]
public sealed class Led : Placeable
{
/// <inheritdoc />
#region Properties & Fields
/// <summary>
/// Represents a single LED of a RGB-device.
/// Gets the <see cref="IRGBDevice"/> this <see cref="Led"/> is associated with.
/// </summary>
[DebuggerDisplay("{Id} {Color}")]
public class Led : AbstractBindable
public IRGBDevice Device { get; }
/// <summary>
/// Gets the <see cref="LedId"/> of the <see cref="Led" />.
/// </summary>
public LedId Id { get; }
private Shape _shape = Shape.Rectangle;
/// <summary>
/// Gets or sets the <see cref="Core.Shape"/> of the <see cref="Led"/>.
/// </summary>
public Shape Shape
{
#region Properties & Fields
/// <summary>
/// Gets the <see cref="IRGBDevice"/> this <see cref="Led"/> is associated with.
/// </summary>
public IRGBDevice Device { get; }
/// <summary>
/// Gets the <see cref="LedId"/> of the <see cref="Led" />.
/// </summary>
public LedId Id { get; }
private Shape _shape = Shape.Rectangle;
/// <summary>
/// Gets or sets the <see cref="Core.Shape"/> of the <see cref="Led"/>.
/// </summary>
public Shape Shape
{
get => _shape;
set => SetProperty(ref _shape, value);
}
private string _shapeData;
/// <summary>
/// Gets or sets the data used for by the <see cref="Core.Shape.Custom"/>-<see cref="Core.Shape"/>.
/// </summary>
public string ShapeData
{
get => _shapeData;
set => SetProperty(ref _shapeData, value);
}
private Point _location;
/// <summary>
/// Gets or sets the relative location of the <see cref="Led"/>.
/// </summary>
public Point Location
{
get => _location;
set
{
if (SetProperty(ref _location, value))
{
UpdateActualData();
UpdateAbsoluteData();
}
}
}
private Size _size;
/// <summary>
/// Gets or sets the size of the <see cref="Led"/>.
/// </summary>
public Size Size
{
get => _size;
set
{
if (SetProperty(ref _size, value))
{
UpdateActualData();
UpdateAbsoluteData();
}
}
}
private Point _actualLocation;
/// <summary>
/// Gets the actual location of the <see cref="Led"/>.
/// This includes device-scaling and rotation.
/// </summary>
public Point ActualLocation
{
get => _actualLocation;
private set => SetProperty(ref _actualLocation, value);
}
private Size _actualSize;
/// <summary>
/// Gets the actual size of the <see cref="Led"/>.
/// This includes device-scaling.
/// </summary>
public Size ActualSize
{
get => _actualSize;
private set => SetProperty(ref _actualSize, value);
}
private Rectangle _ledRectangle;
/// <summary>
/// Gets a rectangle representing the logical location of the <see cref="Led"/> relative to the <see cref="Device"/>.
/// </summary>
public Rectangle LedRectangle
{
get => _ledRectangle;
private set => SetProperty(ref _ledRectangle, value);
}
private Rectangle _absoluteLedRectangle;
/// <summary>
/// Gets a rectangle representing the logical location of the <see cref="Led"/> on the <see cref="RGBSurface"/>.
/// </summary>
public Rectangle AbsoluteLedRectangle
{
get => _absoluteLedRectangle;
private set => SetProperty(ref _absoluteLedRectangle, value);
}
/// <summary>
/// Indicates whether the <see cref="Led" /> is about to change it's color.
/// </summary>
public bool IsDirty => RequestedColor.HasValue && (RequestedColor != InternalColor);
private Color? _requestedColor;
/// <summary>
/// Gets a copy of the <see cref="Core.Color"/> the LED should be set to on the next update.
/// Null if there is no update-request for the next update.
/// </summary>
public Color? RequestedColor
{
get => _requestedColor;
private set
{
SetProperty(ref _requestedColor, value);
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(IsDirty));
}
}
private Color _color = Color.Transparent;
/// <summary>
/// Gets the current <see cref="Core.Color"/> of the <see cref="Led"/>. Sets the <see cref="RequestedColor" /> for the next update.
/// </summary>
public Color Color
{
get => _color;
set
{
if (!IsLocked)
{
if (RequestedColor.HasValue)
RequestedColor += value;
else
RequestedColor = value;
}
}
}
/// <summary>
/// Gets or set the <see cref="Color"/> ignoring all workflows regarding locks and update-requests. />
/// </summary>
internal Color InternalColor
{
get => _color;
set => SetProperty(ref _color, value);
}
private bool _isLocked;
/// <summary>
/// Gets or sets if the color of this LED can be changed.
/// </summary>
public bool IsLocked
{
get => _isLocked;
set => SetProperty(ref _isLocked, value);
}
/// <summary>
/// Gets the URI of an image of the <see cref="Led"/> or null if there is no image.
/// </summary>
public Uri Image { get; set; }
/// <summary>
/// Gets the provider-specific data associated with this led.
/// </summary>
public object CustomData { get; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="Led"/> class.
/// </summary>
/// <param name="device">The <see cref="IRGBDevice"/> the <see cref="Led"/> is associated with.</param>
/// <param name="id">The <see cref="LedId"/> of the <see cref="Led"/>.</param>
/// <param name="location">The physical location of the <see cref="Led"/> relative to the <see cref="Device"/>.</param>
/// <param name="size">The size of the <see cref="Led"/>.</param>
/// <param name="customData">The provider-specific data associated with this led.</param>
internal Led(IRGBDevice device, LedId id, Point location, Size size, object customData = null)
{
this.Device = device;
this.Id = id;
this.Location = location;
this.Size = size;
this.CustomData = customData;
device.PropertyChanged += DevicePropertyChanged;
}
#endregion
#region Methods
private void DevicePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if ((e.PropertyName == nameof(IRGBDevice.Location)))
UpdateAbsoluteData();
else if (e.PropertyName == nameof(IRGBDevice.DeviceRectangle))
{
UpdateActualData();
UpdateAbsoluteData();
}
}
private void UpdateActualData()
{
ActualSize = Size * Device.Scale;
Point actualLocation = (Location * Device.Scale);
Rectangle ledRectangle = new Rectangle(Location * Device.Scale, Size * Device.Scale);
if (Device.Rotation.IsRotated)
{
Point deviceCenter = new Rectangle(Device.ActualSize).Center;
Point actualDeviceCenter = new Rectangle(Device.DeviceRectangle.Size).Center;
Point centerOffset = new Point(actualDeviceCenter.X - deviceCenter.X, actualDeviceCenter.Y - deviceCenter.Y);
actualLocation = actualLocation.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center) + centerOffset;
ledRectangle = new Rectangle(ledRectangle.Rotate(Device.Rotation, new Rectangle(Device.ActualSize).Center)).Translate(centerOffset);
}
ActualLocation = actualLocation;
LedRectangle = ledRectangle;
}
private void UpdateAbsoluteData()
{
AbsoluteLedRectangle = LedRectangle.Translate(Device.Location);
}
/// <summary>
/// Converts the <see cref="Id"/> and the <see cref="Color"/> of this <see cref="Led"/> to a human-readable string.
/// </summary>
/// <returns>A string that contains the <see cref="Id"/> and the <see cref="Color"/> of this <see cref="Led"/>. For example "Enter [A: 255, R: 255, G: 0, B: 0]".</returns>
public override string ToString() => $"{Id} {Color}";
/// <summary>
/// Updates the <see cref="Led"/> to the requested <see cref="Core.Color"/>.
/// </summary>
internal void Update()
{
if (!RequestedColor.HasValue) return;
_color = RequestedColor.Value;
RequestedColor = null;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(Color));
}
/// <summary>
/// Resets the <see cref="Led"/> back to default.
/// </summary>
internal void Reset()
{
_color = Color.Transparent;
RequestedColor = null;
IsLocked = false;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(Color));
}
#endregion
#region Operators
/// <summary>
/// Converts a <see cref="Led" /> to a <see cref="Core.Color" />.
/// </summary>
/// <param name="led">The <see cref="Led"/> to convert.</param>
public static implicit operator Color(Led led) => led?.Color ?? Color.Transparent;
/// <summary>
/// Converts a <see cref="Led" /> to a <see cref="Rectangle" />.
/// </summary>
/// <param name="led">The <see cref="Led"/> to convert.</param>
public static implicit operator Rectangle(Led led) => led?.LedRectangle ?? new Rectangle();
#endregion
get => _shape;
set => SetProperty(ref _shape, value);
}
}
private string? _shapeData;
/// <summary>
/// Gets or sets the data used for by the <see cref="Core.Shape.Custom"/>-<see cref="Core.Shape"/>.
/// </summary>
public string? ShapeData
{
get => _shapeData;
set => SetProperty(ref _shapeData, value);
}
private Rectangle _absoluteBoundary;
/// <summary>
/// Gets a rectangle representing the logical location of the <see cref="Led"/> on the <see cref="RGBSurface"/>.
/// </summary>
public Rectangle AbsoluteBoundary
{
get => _absoluteBoundary;
private set => SetProperty(ref _absoluteBoundary, value);
}
/// <summary>
/// Indicates whether the <see cref="Led" /> is about to change it's color.
/// </summary>
public bool IsDirty => RequestedColor.HasValue && (RequestedColor != Color);
private Color? _requestedColor;
/// <summary>
/// Gets a copy of the <see cref="Core.Color"/> the LED should be set to on the next update.
/// Null if there is no update-request for the next update.
/// </summary>
public Color? RequestedColor
{
get => _requestedColor;
private set
{
SetProperty(ref _requestedColor, value);
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(IsDirty));
}
}
private Color _color = Color.Transparent;
/// <summary>
/// Gets the current <see cref="Core.Color"/> of the <see cref="Led"/>. Sets the <see cref="RequestedColor" /> for the next update.
/// </summary>
public Color Color
{
get => _color;
set
{
if (RequestedColor.HasValue)
RequestedColor = RequestedColor.Value + value;
else
RequestedColor = _color + value;
}
}
/// <summary>
/// Gets the provider-specific data associated with this led.
/// </summary>
public object? CustomData { get; }
/// <summary>
/// Gets or sets some custom metadata of this led.
/// </summary>
public object? LayoutMetadata { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="Led"/> class.
/// </summary>
/// <param name="device">The <see cref="IRGBDevice"/> the <see cref="Led"/> is associated with.</param>
/// <param name="id">The <see cref="LedId"/> of the <see cref="Led"/>.</param>
/// <param name="location">The physical location of the <see cref="Led"/> relative to the <see cref="Device"/>.</param>
/// <param name="size">The size of the <see cref="Led"/>.</param>
/// <param name="customData">The provider-specific data associated with this led.</param>
internal Led(IRGBDevice device, LedId id, Point location, Size size, object? customData = null)
: base(device)
{
this.Device = device;
this.Id = id;
this.Location = location;
this.Size = size;
this.CustomData = customData;
}
#endregion
#region Methods
/// <inheritdoc />
protected override void UpdateActualPlaceableData()
{
base.UpdateActualPlaceableData();
AbsoluteBoundary = Boundary.Translate(Device.Location);
}
/// <summary>
/// Converts the <see cref="Id"/> and the <see cref="Color"/> of this <see cref="Led"/> to a human-readable string.
/// </summary>
/// <returns>A string that contains the <see cref="Id"/> and the <see cref="Color"/> of this <see cref="Led"/>. For example "Enter [A: 255, R: 255, G: 0, B: 0]".</returns>
public override string ToString() => $"{Id} {Color}";
/// <summary>
/// Updates the <see cref="Led"/> to the requested <see cref="Core.Color"/>.
/// </summary>
internal void Update()
{
if (!RequestedColor.HasValue) return;
_color = RequestedColor.Value;
RequestedColor = null;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(nameof(Color));
}
#endregion
#region Operators
/// <summary>
/// Converts a <see cref="Led" /> to a <see cref="Core.Color" />.
/// </summary>
/// <param name="led">The <see cref="Led"/> to convert.</param>
public static implicit operator Color(Led led) => led.Color;
/// <summary>
/// Converts a <see cref="Led" /> to a <see cref="Rectangle" />.
/// </summary>
/// <param name="led">The <see cref="Led"/> to convert.</param>
public static implicit operator Rectangle(Led led) => led.Boundary;
#endregion
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core;
/// <summary>
/// Represents a mapping from <see cref="LedId"/> to a custom identifier.
/// </summary>
/// <typeparam name="T">The identifier the <see cref="LedId"/> is mapped to.</typeparam>
public sealed class LedMapping<T> : IEnumerable<(LedId ledId, T mapping)>
where T : notnull
{
#region Constants
public static LedMapping<T> Empty { get; } = [];
#endregion
#region Properties & Fields
private readonly Dictionary<LedId, T> _mapping = [];
private readonly Dictionary<T, LedId> _reverseMapping = [];
/// <summary>
/// Gets the number of entries in this mapping.
/// </summary>
public int Count => _mapping.Count;
/// <summary>
/// Gets a collection of all mapped ledids.
/// </summary>
public ICollection<LedId> LedIds => _mapping.Keys;
/// <summary>
/// Gets a collection of all mapped custom identifiers.
/// </summary>
public ICollection<T> Mappings => _reverseMapping.Keys;
#endregion
#region Indexer
/// <summary>
/// Gets the custom identifier mapped to the specified <see cref="LedId"/>.
/// </summary>
/// <param name="ledId">The led id to get the mapped identifier.</param>
/// <returns>The mapped ifentifier.</returns>
public T this[LedId ledId]
{
get => _mapping[ledId];
set
{
_mapping[ledId] = value;
_reverseMapping[value] = ledId;
}
}
/// <summary>
/// Gets the <see cref="LedId"/> mapped to the specified custom identifier.
/// </summary>
/// <param name="mapping">The custom identifier to get the mapped led id.</param>
/// <returns>The led id.</returns>
public LedId this[T mapping]
{
get => _reverseMapping[mapping];
set => this[value] = mapping;
}
#endregion
#region Methods
/// <summary>
/// Adds a new entry to the mapping.
/// </summary>
/// <param name="ledId">The <see cref="LedId"/> to map.</param>
/// <param name="mapping">The custom identifier to map.</param>
public void Add(LedId ledId, T mapping)
{
_mapping.Add(ledId, mapping);
_reverseMapping.Add(mapping, ledId);
}
/// <summary>
/// Checks if the specified <see cref="LedId"/> is mapped.
/// </summary>
/// <param name="ledId">The led id to check.</param>
/// <returns><c>true</c> if the led id is mapped; otherwise <c>false</c>.</returns>
public bool Contains(LedId ledId) => _mapping.ContainsKey(ledId);
/// <summary>
/// Checks if the specified custom identifier is mapped.
/// </summary>
/// <param name="mapping">The custom identifier to check.</param>
/// <returns><c>true</c> if the led id is mapped; otherwise <c>false</c>.</returns>
public bool Contains(T mapping) => _reverseMapping.ContainsKey(mapping);
/// <summary>
/// Gets the custom identifier mapped to the specified led id.
/// </summary>
/// <param name="ledId">The led id to get the custom identifier for.</param>
/// <param name="mapping">Contains the mapped custom identifier or null if there is no mapping for the specified led id.</param>
/// <returns><c>true</c> if there was a custom identifier for the specified led id; otherwise <c>false</c>.</returns>
public bool TryGetValue(LedId ledId, out T? mapping) => _mapping.TryGetValue(ledId, out mapping);
/// <summary>
/// Gets the led id mapped to the specified custom identifier.
/// </summary>
/// <param name="mapping">The custom identifier to get the led id for.</param>
/// <param name="ledId">Contains the mapped led id or null if there is no mapping for the specified led id.</param>
/// <returns><c>true</c> if there was a led id for the specified custom identifier; otherwise <c>false</c>.</returns>
public bool TryGetValue(T mapping, out LedId ledId) => _reverseMapping.TryGetValue(mapping, out ledId);
/// <summary>
/// Removes the specified led id and the mapped custom identifier.
/// </summary>
/// <param name="ledId">The led id to remove.</param>
/// <returns><c>true</c> if there was a mapping for the led id to remove; otherwise <c>false</c>.</returns>
public bool Remove(LedId ledId)
{
if (_mapping.TryGetValue(ledId, out T? mapping))
_reverseMapping.Remove(mapping);
return _mapping.Remove(ledId);
}
/// <summary>
/// Removes the specified custom identifier and the mapped led id.
/// </summary>
/// <param name="mapping">The custom identifier to remove.</param>
/// <returns><c>true</c> if there was a mapping for the custom identifier to remove; otherwise <c>false</c>.</returns>
public bool Remove(T mapping)
{
if (_reverseMapping.TryGetValue(mapping, out LedId ledId))
_mapping.Remove(ledId);
return _reverseMapping.Remove(mapping);
}
/// <summary>
/// Removes all registered mappings.
/// </summary>
public void Clear()
{
_mapping.Clear();
_reverseMapping.Clear();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <inheritdoc />
public IEnumerator<(LedId ledId, T mapping)> GetEnumerator() => _mapping.Select(x => (x.Key, x.Value)).GetEnumerator();
#endregion
}

View File

@ -1,67 +1,62 @@
using System.ComponentModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace RGB.NET.Core
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents a basic bindable class which notifies when a property value changes.
/// </summary>
public abstract class AbstractBindable : IBindable
{
/// <inheritdoc />
#region Events
/// <summary>
/// Represents a basic bindable class which notifies when a property value changes.
/// Occurs when a property value changes.
/// </summary>
public abstract class AbstractBindable : IBindable
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
#region Methods
/// <summary>
/// Checks if the property already matches the desired value or needs to be updated.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to the backing-filed.</param>
/// <param name="value">Value to apply.</param>
/// <returns><c>true</c> if the value needs to be updated; otherwise <c>false</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool RequiresUpdate<T>(ref T storage, T value) => !EqualityComparer<T>.Default.Equals(storage, value);
/// <summary>
/// Checks if the property already matches the desired value and updates it if not.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to the backing-filed.</param>
/// <param name="value">Value to apply.</param>
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
/// <returns><c>true</c> if the value was changed, <c>false</c> if the existing value matched the desired value.</returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)
{
#region Events
if (!RequiresUpdate(ref storage, value)) return false;
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Methods
/// <summary>
/// Checks if the property already matches the desirec value or needs to be updated.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to the backing-filed.</param>
/// <param name="value">Value to apply.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual bool RequiresUpdate<T>(ref T storage, T value)
{
return !Equals(storage, value);
}
/// <summary>
/// Checks if the property already matches the desired value and updates it if not.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to the backing-filed.</param>
/// <param name="value">Value to apply.</param>
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
/// <returns><c>true</c> if the value was changed, <c>false</c> if the existing value matched the desired value.</returns>
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (!this.RequiresUpdate(ref storage, value)) return false;
storage = value;
// ReSharper disable once ExplicitCallerInfoArgument
this.OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Triggers the <see cref="PropertyChanged"/>-event when a a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
storage = value;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propertyName);
return true;
}
}
/// <summary>
/// Triggers the <see cref="PropertyChanged"/>-event when a a property value has changed.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This value is optional
/// and can be provided automatically when invoked from compilers that support <see cref="CallerMemberNameAttribute"/>.</param>
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
}

Some files were not shown because too many files have changed in this diff Show More