1
0
mirror of https://github.com/DarthAffe/RGB.NET.git synced 2025-12-13 10:08: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="Description" type="xsd:string" />
<xsd:element name="Author" type="xsd:string" /> <xsd:element name="Author" type="xsd:string" />
<xsd:element name="Type" 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="Vendor" type="xsd:string" />
<xsd:element name="Model" type="xsd:string" /> <xsd:element name="Model" type="xsd:string" />
<xsd:element name="Shape" type="xsd:string" /> <xsd:element name="Shape" type="xsd:string" />
<xsd:element name="Width" type="xsd:double" /> <xsd:element name="Width" type="xsd:double" />
<xsd:element name="Height" 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="LedUnitWidth" type="xsd:double" />
<xsd:element name="LedUnitHeight" 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:complexType>
<xsd:sequence> <xsd:sequence>
<xsd:element maxOccurs="unbounded" name="Led"> <xsd:element maxOccurs="unbounded" name="Led">
@ -28,6 +33,13 @@
<xsd:element name="Y" type="xsd:string" /> <xsd:element name="Y" type="xsd:string" />
<xsd:element name="Width" type="xsd:string" /> <xsd:element name="Width" type="xsd:string" />
<xsd:element name="Height" 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:sequence>
<xsd:attribute name="Id" type="xsd:string" use="required" /> <xsd:attribute name="Id" type="xsd:string" use="required" />
</xsd:complexType> </xsd:complexType>
@ -35,32 +47,7 @@
</xsd:sequence> </xsd:sequence>
</xsd:complexType> </xsd:complexType>
</xsd:element> </xsd:element>
<xsd:element name="LedImageLayouts"> </xsd:sequence>
<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:complexType> </xsd:complexType>
</xsd:element> </xsd:element>
</xs:schema> </xs:schema>

View File

@ -1,30 +1,87 @@
# RGB.NET # 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. > **IMPORTANT NOTE**
**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. 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/).
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).
## 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 ## 4. Initialize the providers for all devices you want to use and add the devices to the surface. For example:
This is the easiest and therefore preferred way to include RGB.NET in your project. ```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). 5. Add an update-trigger. In most cases the TimerUpdateTrigger is preferable, but you can also implement your own to fit your needs.
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). ```csharp
surface.RegisterUpdateTrigger(new TimerUpdateTrigger());
```
> If you want to trigger updates manually the `ManualUpdateTrigger` should be used.
### .NET 4.5 Support ### 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)
At the end of the year with the release of .NET 5 the support for old .NET-Framwork versions will be droppped! 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:
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. ```csharp
surface.AlignDevices();
```
The basic setup is now complete and you can start setting up your rendering.
### Device-Layouts ### Basic Rendering
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. 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 3. Add a decorator to the gradient to make it move. (Decorators are
[![Example video](https://img.youtube.com/vi/JLRa0Wv4qso/0.jpg)](http://www.youtube.com/watch?v=JLRa0Wv4qso) ```csharp
rainbow.AddDecorator(new MoveGradientDecorator(surface));
```
#### Example Projects 4. Create a texture (the size - in this example 10, 10 - is not important here since the gradient shoukd be stretched anyway)
[https://github.com/DarthAffe/KeyboardAudioVisualizer](https://github.com/DarthAffe/KeyboardAudioVisualizer) ```csharp
[https://github.com/DarthAffe/RGBSyncPlus](https://github.com/DarthAffe/RGBSyncPlus) 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(); if (blendColor.A.EqualsInTolerance(1))
/// <summary> return blendColor;
/// Gets the singleton instance of <see cref="DefaultColorBehavior"/>.
/// </summary>
public static DefaultColorBehavior Instance { get; } = _instance;
#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 return new Color(resultA, resultR, resultG, resultB);
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
} }
}
#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 /// <summary>
{ /// Converts the specified <see cref="Color"/> to a string representation.
string ToString(Color color); /// </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;
using System.Diagnostics; 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 /> /// <inheritdoc />
/// <summary> /// <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> /// </summary>
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")] /// <param name="r">The red component value of this <see cref="T:RGB.NET.Core.Color" />.</param>
public struct Color /// <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 A = a.Clamp(0, 1);
R = r.Clamp(0, 1);
/// <summary> G = g.Clamp(0, 1);
/// Gets an transparent color [A: 0, R: 0, G: 0, B: 0] B = b.Clamp(0, 1);
/// </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
} }
}
/// <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 // ReSharper disable UnusedMember.Global
using System; 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 (float cHue, float cSaturation, float cValue) = color.GetHSV();
return Create(color.A, cHue + hue, cSaturation + saturation, cValue + value);
/// <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
} }
}
/// <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 // ReSharper disable UnusedMember.Global
using System; 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 if ((hexString == null) || (hexString.Length < 6))
/// <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]);
throw new ArgumentException("Invalid hex string", nameof(hexString)); 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 // ReSharper disable UnusedMember.Global
namespace RGB.NET.Core namespace RGB.NET.Core;
/// <summary>
/// Represents a generic color-correction.
/// </summary>
public interface IColorCorrection
{ {
/// <summary> /// <summary>
/// Represents a generic color-correction. /// Applies the <see cref="IColorCorrection"/> to the specified <see cref="Color"/>.
/// </summary> /// </summary>
public interface IColorCorrection /// <param name="color">The <see cref="Color"/> to correct.</param>
{ void ApplyTo(ref Color color);
/// <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);
}
}

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.Collections.ObjectModel;
using System.Linq; 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" /> #region Properties & Fields
/// <inheritdoc cref="IDecoratable{T}" />
public abstract class AbstractDecoratable<T> : AbstractBindable, IDecoratable<T> private readonly List<T> _decorators = [];
where T : IDecorator
/// <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 Decorators = new ReadOnlyCollection<T>(_decorators);
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
} }
}
#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.Collections.Generic;
using System.Linq; 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" /> #region Properties & Fields
/// <inheritdoc cref="IDecorator" />
public abstract class AbstractDecorator : AbstractBindable, IDecorator private bool _isEnabled = true;
/// <inheritdoc />
public bool IsEnabled
{ {
#region Properties & Fields get => _isEnabled;
set => SetProperty(ref _isEnabled, value);
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
} }
}
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> /// <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> /// </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 this.Surface = surface;
this.UpdateIfDisabled = updateIfDisabled;
/// <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
} }
}
#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> /// <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> /// </summary>
public interface IBrushDecorator : IDecorator /// <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>
/// <summary> /// <param name="color">The <see cref="Color"/> to be modified.</param>
/// Decorator-Method called by the <see cref="IBrush"/>. void ManipulateColor(Rectangle rectangle, RenderTarget renderTarget, ref Color color);
/// </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);
}
}

View File

@ -1,42 +1,40 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; 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> /// <summary>
/// Represents a basic decoratable. /// Gets a readonly-list of all <see cref="IDecorator"/> attached to this <see cref="IDecoratable{T}"/>.
/// </summary> /// </summary>
public interface IDecoratable : INotifyPropertyChanged IReadOnlyList<T> Decorators { get; }
{ }
/// <inheritdoc />
/// <summary> /// <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> /// </summary>
/// <typeparam name="T"></typeparam> /// <param name="decorator">The <see cref="IDecorator"/> to be added.</param>
public interface IDecoratable<T> : IDecoratable void AddDecorator(T decorator);
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; }
/// <summary> /// <summary>
/// Adds an <see cref="IDecorator"/> to the <see cref="IDecoratable"/>. /// Removes an <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
/// </summary> /// </summary>
/// <param name="decorator">The <see cref="IDecorator"/> to be added.</param> /// <param name="decorator">The <see cref="IDecorator"/> to be removed.</param>
void AddDecorator(T decorator); void RemoveDecorator(T decorator);
/// <summary> /// <summary>
/// Removes an <see cref="IDecorator"/> from the <see cref="IDecoratable"/>. /// Removes all <see cref="IDecorator"/> from the <see cref="IDecoratable"/>.
/// </summary> /// </summary>
/// <param name="decorator">The <see cref="IDecorator"/> to be removed.</param> void RemoveAllDecorators();
void RemoveDecorator(T decorator); }
/// <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> /// <summary>
/// Represents a basic decorator. /// Gets or sets if the <see cref="IDecorator"/> is enabled and will be used.
/// </summary> /// </summary>
public interface IDecorator bool IsEnabled { get; set; }
{
#region Properties & Fields
/// <summary> /// <summary>
/// Gets or sets if the <see cref="IDecorator"/> is enabled and will be used. /// Gets or sets the order in which multiple decorators should be applied on the same object.
/// </summary> /// Higher orders are processed first.
bool IsEnabled { get; set; } /// </summary>
int Order { get; set; }
/// <summary> #endregion
/// 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 #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> /// <summary>
/// Attaches this <see cref="IDecorator"/> to the given target. /// Detaches this <see cref="IDecorator"/> from the specified target.
/// </summary> /// </summary>
/// <param name="decoratable">The object this <see cref="IDecorator"/> should be attached to.</param> /// <param name="decoratable">The object this <see cref="IDecorator"/> should be detached from.</param>
void OnAttached(IDecoratable decoratable); void OnDetached(IDecoratable decoratable);
/// <summary> #endregion
/// 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
}
}

View File

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

View File

@ -3,296 +3,246 @@
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using System; using System;
using System.Buffers;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; 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" /> private RGBSurface? _surface;
/// <inheritdoc cref="IRGBDevice{TDeviceInfo}" />
/// <summary> #region Properties & Fields
/// Represents a generic RGB-device.
/// </summary> RGBSurface? IRGBDevice.Surface
public abstract class AbstractRGBDevice<TDeviceInfo> : AbstractBindable, IRGBDevice<TDeviceInfo>
where TDeviceInfo : class, IRGBDeviceInfo
{ {
#region Properties & Fields get => _surface;
set
/// <inheritdoc />
public abstract TDeviceInfo DeviceInfo { get; }
/// <inheritdoc />
IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo;
private Point _location = new Point(0, 0);
/// <inheritdoc />
public Point Location
{ {
get => _location; if (SetProperty(ref _surface, value))
set
{ {
if (SetProperty(ref _location, value)) if (value == null) OnDetached();
UpdateActualData(); 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;
using System.Collections.Generic; 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}" /> #region Properties
/// <inheritdoc cref="IBindable" />
/// <inheritdoc cref="IDisposable" />
/// <summary> /// <summary>
/// Represents a generic RGB-device. /// Gets the surface this device is attached to.
/// </summary> /// </summary>
public interface IRGBDevice : IEnumerable<Led>, IBindable, IDisposable RGBSurface? Surface { get; internal set; }
{
#region Properties
/// <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> /// <summary>
/// Represents a generic RGB-device with an known device-info type. /// Gets generic information about the <see cref="IRGBDevice"/>.
/// </summary> /// </summary>
public interface IRGBDevice<out TDeviceInfo> : IRGBDevice IRGBDeviceInfo DeviceInfo { get; }
where TDeviceInfo : IRGBDeviceInfo
{ /// <summary>
/// <summary> /// Gets a list of color corrections applied to this device.
/// Gets generic information about the <see cref="IRGBDevice"/>. /// </summary>
/// </summary> IList<IColorCorrection> ColorCorrections { get; }
new TDeviceInfo DeviceInfo { 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> /// <summary>
/// Represents a generic information for a <see cref="IRGBDevice"/> /// Gets the <see cref="RGBDeviceType"/> of the <see cref="IRGBDevice"/>.
/// </summary> /// </summary>
public interface IRGBDeviceInfo RGBDeviceType DeviceType { get; }
{
#region Properties & Fields
/// <summary> /// <summary>
/// Gets the <see cref="RGBDeviceType"/> of the <see cref="IRGBDevice"/>. /// Unique name of the <see cref="IRGBDevice"/>.
/// </summary> /// </summary>
RGBDeviceType DeviceType { get; } string DeviceName { get; }
/// <summary> /// <summary>
/// Unique name of the <see cref="IRGBDevice"/>. /// Gets the manufacturer-name of the <see cref="IRGBDevice"/>.
/// </summary> /// </summary>
string DeviceName { get; } string Manufacturer { get; }
/// <summary> /// <summary>
/// Gets the manufacturer-name of the <see cref="IRGBDevice"/>. /// Gets the model-name of the <see cref="IRGBDevice"/>.
/// </summary> /// </summary>
string Manufacturer { get; } string Model { get; }
/// <summary> /// <summary>
/// Gets the model-name of the <see cref="IRGBDevice"/>. /// Gets custom metadata added to the layout.
/// </summary> /// </summary>
string Model { get; } object? LayoutMetadata { get; set; }
/// <summary> #endregion
/// 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
}
}

View File

@ -1,48 +1,66 @@
using System; // ReSharper disable EventNeverSubscribedTo.Global
using System;
using System.Collections.Generic; 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> /// <summary>
/// Represents a generic device provider. /// Indicates if the used SDK is initialized and ready to use.
/// </summary> /// </summary>
public interface IRGBDeviceProvider : IDisposable bool IsInitialized { get; }
{
#region Properties & Fields
/// <summary> /// <summary>
/// Indicates if the used SDK is initialized and ready to use. /// Indicates if exceptions in the device provider are thrown or silently ignored.
/// </summary> /// </summary>
bool IsInitialized { get; } /// <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> /// <summary>
/// Gets a list of <see cref="IRGBDevice"/> loaded by this <see cref="IRGBDeviceProvider"/>. /// Gets a collection of <see cref="IRGBDevice"/> loaded by this <see cref="IRGBDeviceProvider"/>.
/// </summary> /// </summary>
IEnumerable<IRGBDevice> Devices { get; } IReadOnlyList<IRGBDevice> Devices { get; }
/// <summary> /// <summary>
/// Gets whether the application has exclusive access to devices or not. /// Gets a collection <see cref="IDeviceUpdateTrigger"/> registered to this device provider.
/// </summary> /// </summary>
bool HasExclusiveAccess { get; } IReadOnlyList<(int id, IDeviceUpdateTrigger trigger)> UpdateTriggers { get; }
#endregion #endregion
#region Methods #region Events
/// <summary> /// <summary>
/// Initializes the <see cref="IRGBDeviceProvider"/> if not already happened or reloads it if it is already initialized. /// Occurs when an exception is thrown in the device provider.
/// </summary> /// </summary>
/// <param name="loadFilter">Specifies which types of devices to load.</param> event EventHandler<ExceptionEventArgs>? Exception;
/// <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> /// <summary>
/// Resets all handled <see cref="IRGBDevice"/> back top default. /// Occures when the devices provided by this device provider changed.
/// </summary> /// </summary>
void ResetDevices(); 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; 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> /// <summary>
/// Contains a list of different types of device. /// Represents nothing.
/// </summary> /// </summary>
[Flags] None = 0,
public enum RGBDeviceType
{
/// <summary>
/// Represents nothing.
/// </summary>
None = 0,
/// <summary> /// <summary>
/// Represents a keyboard. /// Represents a keyboard.
/// </summary> /// </summary>
Keyboard = 1 << 0, Keyboard = 1 << 0,
/// <summary> /// <summary>
/// Represents a mouse. /// Represents a mouse.
/// </summary> /// </summary>
Mouse = 1 << 1, Mouse = 1 << 1,
/// <summary> /// <summary>
/// Represents a headset. /// Represents a headset.
/// </summary> /// </summary>
Headset = 1 << 2, Headset = 1 << 2,
/// <summary> /// <summary>
/// Represents a mousepad. /// Represents a mousepad.
/// </summary> /// </summary>
Mousepad = 1 << 3, Mousepad = 1 << 3,
/// <summary> /// <summary>
/// Represents a LED-stipe. /// Represents a LED-stipe.
/// </summary> /// </summary>
LedStripe = 1 << 4, LedStripe = 1 << 4,
/// <summary> /// <summary>
/// Represents a LED-matrix. /// Represents a LED-matrix.
/// </summary> /// </summary>
LedMatrix = 1 << 5, LedMatrix = 1 << 5,
/// <summary> /// <summary>
/// Represents a Mainboard. /// Represents a Mainboard.
/// </summary> /// </summary>
Mainboard = 1 << 6, Mainboard = 1 << 6,
/// <summary> /// <summary>
/// Represents a Graphics card. /// Represents a Graphics card.
/// </summary> /// </summary>
GraphicsCard = 1 << 7, GraphicsCard = 1 << 7,
/// <summary> /// <summary>
/// Represents a DRAM-bank. /// Represents a DRAM-bank.
/// </summary> /// </summary>
DRAM = 1 << 8, DRAM = 1 << 8,
/// <summary> /// <summary>
/// Represents a headset stand. /// Represents a headset stand.
/// </summary> /// </summary>
HeadsetStand = 1 << 9, HeadsetStand = 1 << 9,
/// <summary> /// <summary>
/// Represents a keypad. /// Represents a keypad.
/// </summary> /// </summary>
Keypad = 1 << 10, Keypad = 1 << 10,
/// <summary> /// <summary>
/// Represents a fan. /// Represents a fan.
/// </summary> /// </summary>
Fan = 1 << 11, Fan = 1 << 11,
/// <summary> /// <summary>
/// Represents a speaker /// Represents a speaker
/// </summary> /// </summary>
Speaker = 1 << 12, Speaker = 1 << 12,
/// <summary> /// <summary>
/// Represents a cooler. /// Represents a cooler.
/// </summary> /// </summary>
Cooler = 1 << 13, Cooler = 1 << 13,
/// <summary> /// <summary>
/// Represents a device where the type is not known or not present in the list. /// Represents a monitor.
/// </summary> /// </summary>
Unknown = 1 << 31, Monitor = 1 << 14,
/// <summary> /// <summary>
/// Represents all devices. /// Represents a generic led-controller.
/// </summary> /// </summary>
All = ~None 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 namespace RGB.NET.Core;
{
/// <summary> /// <summary>
/// Represents a cooler-device /// Represents a cooler-device
/// </summary> /// </summary>
public interface ICooler : IRGBDevice public interface ICooler : IRGBDevice;
{ }
}

View File

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

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core namespace RGB.NET.Core;
{
/// <summary> /// <summary>
/// represents a fan-device /// represents a fan-device
/// </summary> /// </summary>
public interface IFan : IRGBDevice 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 namespace RGB.NET.Core;
{
/// <summary> /// <summary>
/// Represents a graphics-card-device /// Represents a graphics-card-device
/// </summary> /// </summary>
public interface IGraphicsCard : IRGBDevice public interface IGraphicsCard : IRGBDevice;
{ }
}

View File

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

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core namespace RGB.NET.Core;
{
/// <summary> /// <summary>
/// Represents a headset-stand-device /// Represents a headset-stand-device
/// </summary> /// </summary>
public interface IHeadsetStand : IRGBDevice 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> /// <summary>
/// Represents a keyboard-device /// Gets the device information assiciated with this device.
/// </summary> /// </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 namespace RGB.NET.Core;
{
/// <summary> /// <summary>
/// Represents a keypad-device /// Represents a keypad-device
/// </summary> /// </summary>
public interface IKeypad : IRGBDevice public interface IKeypad : IRGBDevice;
{ }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,6 @@
namespace RGB.NET.Core namespace RGB.NET.Core;
{
/// <summary> /// <summary>
/// Represents a device with unkown or not specified type. /// Represents a device with unkown or not specified type.
/// </summary> /// </summary>
public interface IUnknownDevice : IRGBDevice 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; 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 /> /// <inheritdoc />
/// <summary> /// <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> /// </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 this.Exception = exception;
this.IsCritical = isCritical;
/// <summary> this.Throw = @throw;
/// 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
} }
}
#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 // ReSharper disable UnusedAutoPropertyAccessor.Global
using System; 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 /> /// <inheritdoc />
/// <summary> /// <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> /// </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 this.Devices = devices;
this.DeviceAdded = deviceAdded;
/// <summary> this.DeviceRemoved = deviceRemoved;
/// Gets the <see cref="IRGBDevice"/> that caused the change. Returns null if the change isn't caused by a <see cref="IRGBDevice"/>. this.DeviceChanged = deviceChanged;
/// </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
} }
}
#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; using System;
namespace RGB.NET.Core namespace RGB.NET.Core;
{
/// <inheritdoc /> /// <inheritdoc />
/// <summary> /// <summary>
/// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updated" />-event. /// Represents the information supplied with an <see cref="E:RGB.NET.Core.RGBSurface.Updated" />-event.
/// </summary> /// </summary>
public class UpdatedEventArgs : EventArgs public class UpdatedEventArgs : EventArgs;
{ }
}

View File

@ -3,49 +3,48 @@
using System; 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 /> /// <inheritdoc />
/// <summary> /// <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> /// </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 this.DeltaTime = deltaTime;
this.Trigger = trigger;
/// <summary> this.CustomData = customData;
/// 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
} }
}
#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; 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 /> /// <inheritdoc />
/// <summary> /// <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> /// </summary>
public class RGBDeviceException : ApplicationException /// <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>
#region Constructors public RGBDeviceException(string message, Exception? innerException = null)
: base(message, innerException)
{ }
/// <inheritdoc /> #endregion
/// <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
}
}

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; 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> long rmean = (r1 + r2) / 2;
/// Calculates the distance between the two given colors using the redmean algorithm. long r = r1 - r2;
/// For more infos check https://www.compuphase.com/cmetric.htm long g = g1 - g2;
/// </summary> long b = b1 - b2;
/// <param name="color1">The start color of the distance calculation.</param> return Math.Sqrt((((512 + rmean) * r * r) >> 8) + (4 * g * g) + (((767 - rmean) * b * b) >> 8));
/// <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
} }
}
#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;
using System.Runtime.CompilerServices; 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> /// <summary>
/// Offers some extensions and helper-methods for the work with doubles /// Defines the precision RGB.NET processes floating point comparisons in.
/// </summary> /// </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 // ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
if (value < min) return min;
/// <summary> if (value > max) return max;
/// Defines the precision RGB.NET processes floating point comparisons in. return value;
/// </summary> // ReSharper restore ConvertIfStatementToReturnStatement
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
} }
}
/// <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; 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> float x = point.X - origin.X;
/// Moves the specified <see cref="Point"/> by the given amount. float y = point.Y - origin.Y;
/// </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);
/// <summary> x = (x * cos) - (y * sin);
/// Rotates the specified <see cref="Point"/> by the given amuont around the given origin. y = (x * sin) + (y * cos);
/// </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);
point = new Point(point.X - origin.X, point.Y - origin.Y); return new Point(x + origin.X, 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
} }
}
#endregion
}

View File

@ -1,169 +1,177 @@
using System; 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> Rectangle intersection = rect.CalculateIntersection(intersectingRect);
/// Sets the <see cref="Rectangle.Location"/> of the given rectangle. return (intersection.Size.Width * intersection.Size.Height) / (rect.Size.Width * rect.Size.Height);
/// </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
} }
}
/// <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}" /> #region Properties & Fields
/// <inheritdoc cref="ILedGroup" />
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> /// <summary>
/// Represents a generic <see cref="T:RGB.NET.Core.AbstractLedGroup" />. /// Initializes a new instance of the <see cref="AbstractLedGroup"/> class.
/// </summary> /// </summary>
public abstract class AbstractLedGroup : AbstractDecoratable<ILedGroupDecorator>, ILedGroup protected AbstractLedGroup(RGBSurface? attachTo)
{ {
#region Properties & Fields attachTo?.Attach(this);
/// <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
} }
}
#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 UnusedMemberInSuper.Global
// ReSharper disable UnusedMember.Global // ReSharper disable UnusedMember.Global
using System;
using System.Collections.Generic; 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> /// <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> /// </summary>
public interface ILedGroup : IDecoratable<ILedGroupDecorator> RGBSurface? Surface { get; internal set; }
{
/// <summary>
/// Gets or sets the <see cref="IBrush"/> which should be drawn over this <see cref="ILedGroup"/>.
/// </summary>
IBrush Brush { get; set; }
/// <summary> /// <summary>
/// Gets or sets the z-index of this <see cref="ILedGroup"/> to allow ordering them before drawing. (lowest first) (default: 0) /// Gets a bool indicating if the group is attached to a surface.
/// </summary> /// </summary>
int ZIndex { get; set; } bool IsAttached => Surface != null;
/// <summary> /// <summary>
/// Gets a list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>. /// Gets or sets the <see cref="IBrush"/> which should be drawn over this <see cref="ILedGroup"/>.
/// </summary> /// </summary>
/// <returns>The list containing all <see cref="Led"/> of this <see cref="ILedGroup"/>.</returns> IBrush? Brush { get; set; }
IList<Led> GetLeds();
/// <summary> /// <summary>
/// Called when the <see cref="ILedGroup"/> is attached to the <see cref="RGBSurface"/>. /// Gets or sets the z-index of this <see cref="ILedGroup"/> to allow ordering them before drawing. (lowest first) (default: 0)
/// </summary> /// </summary>
void OnAttach(); int ZIndex { get; set; }
/// <summary> /// <summary>
/// Called when the <see cref="ILedGroup"/> is detached from the <see cref="RGBSurface"/>. /// Called when the <see cref="ILedGroup"/> is attached to the <see cref="RGBSurface"/>.
/// </summary> /// </summary>
void OnDetach(); 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> /// <summary>
/// Contains helper methods for converting things. /// Converts an array of bytes to a HEX-representation.
/// </summary> /// </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 for (int bx = 0, cx = 0; bx < bytes.Length; ++bx, ++cx)
/// <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)
{ {
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) b = ((byte)(bytes[bx] & 0x0F));
{ c[++cx] = (char)(b > 9 ? b + 0x37 : b + 0x30);
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);
} }
// Source: https://web.archive.org/web/20180224104425/https://stackoverflow.com/questions/623104/byte-to-hex-string/3974535 return new string(c);
/// <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
} }
}
// 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 // ReSharper disable MemberCanBePrivate.Global
using System;
using System.ComponentModel;
using System.Diagnostics; 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> /// <summary>
/// Represents a single LED of a RGB-device. /// Gets the <see cref="IRGBDevice"/> this <see cref="Led"/> is associated with.
/// </summary> /// </summary>
[DebuggerDisplay("{Id} {Color}")] public IRGBDevice Device { get; }
public class Led : AbstractBindable
/// <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 get => _shape;
set => SetProperty(ref _shape, value);
/// <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
} }
}
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; 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> /// <summary>
/// Represents a basic bindable class which notifies when a property value changes. /// Occurs when a property value changes.
/// </summary> /// </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> storage = value;
/// Occurs when a property value changes. // ReSharper disable once ExplicitCallerInfoArgument
/// </summary> OnPropertyChanged(propertyName);
public event PropertyChangedEventHandler PropertyChanged; return true;
#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
} }
}
/// <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