Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 51 additions & 39 deletions KeyLighting/CPUImageProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
private OpenRGB.NET.Color[] resultBuffer;
private bool hasPreviousFrame = false;

private double fadeSpeed;
private double fadeSpeed;

public CPUImageProcessor(LightingConfig config)

Check warning on line 29 in KeyLighting/CPUImageProcessor.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'resultBuffer' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 29 in KeyLighting/CPUImageProcessor.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'rawColors' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 29 in KeyLighting/CPUImageProcessor.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'previousFrame' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
{
fadeSpeed = config.FadeFactor;
}
Expand Down Expand Up @@ -107,12 +107,12 @@
}
else
{

ProcessColumnsWithEffects(targetWidth, brightness, vibrance, contrast, darkThreshold, darkFactor);

if (hasPreviousFrame)
// Apply fading only if fade speed is less than 1.0
if (hasPreviousFrame && fadeSpeed < 1.0)
{
ApplyFading(targetWidth, lastFrameWasSolid ? 0.95 : fadeSpeed);
ApplyFading(targetWidth, fadeSpeed);
}
}

Expand All @@ -134,39 +134,7 @@
}
}

[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private void ProcessSolidColor(byte r, byte g, byte b, int width, double brightness, double vibrance, double contrast, int darkThreshold, double darkFactor)
{

OpenRGB.NET.Color processedColor = FastApplyEffects(r, g, b, brightness, vibrance, contrast, darkThreshold, darkFactor);

bool needsFade = hasPreviousFrame && !(lastFrameWasSolid &&
lastSolidR == processedColor.R &&
lastSolidG == processedColor.G &&
lastSolidB == processedColor.B);

if (needsFade)
{

double fadeFactor = 0.95;

Parallel.For(0, width, i => {
resultBuffer[i] = FastBlendColors(previousFrame[i], processedColor, fadeFactor);
});
}
else
{

for (int i = 0; i < width; i++)
{
resultBuffer[i] = processedColor;
}
}

lastSolidR = processedColor.R;
lastSolidG = processedColor.G;
lastSolidB = processedColor.B;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool AreSettingsCached(double brightness, double contrast)
Expand All @@ -189,13 +157,13 @@
if (rawColors.Length < 2) return true;

OpenRGB.NET.Color first = rawColors[0];
const int tolerance = 5;
const int tolerance = 5;

int[] samplePoints = { 0, rawColors.Length / 3, rawColors.Length / 2, (rawColors.Length * 2) / 3, rawColors.Length - 1 };

foreach (int i in samplePoints)
{
if (i == 0) continue;
if (i == 0) continue;

if (Math.Abs(first.R - rawColors[i].R) > tolerance ||
Math.Abs(first.G - rawColors[i].G) > tolerance ||
Expand All @@ -211,6 +179,9 @@
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private void ApplyFading(int width, double fadeFactor)
{
// This method should only be called when fadeSpeed < 1.0
// Simply use the provided fade factor without any brightness-based adjustments

Parallel.For(0, width, i => {
resultBuffer[i] = FastBlendColors(previousFrame[i], resultBuffer[i], fadeFactor);
});
Expand All @@ -219,17 +190,58 @@
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private OpenRGB.NET.Color FastBlendColors(OpenRGB.NET.Color color1, OpenRGB.NET.Color color2, double factor)
{

factor = Math.Clamp(factor, 0.0, 1.0);
double inverseFactor = 1.0 - factor;

// Process each channel with adaptive blending
byte r = (byte)(color1.R * inverseFactor + color2.R * factor);
byte g = (byte)(color1.G * inverseFactor + color2.G * factor);
byte b = (byte)(color1.B * inverseFactor + color2.B * factor);

return new OpenRGB.NET.Color(r, g, b);
}

// Also modify ProcessSolidColor method to handle brightness transitions better
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private void ProcessSolidColor(byte r, byte g, byte b, int width, double brightness, double vibrance, double contrast, int darkThreshold, double darkFactor)
{
OpenRGB.NET.Color processedColor = FastApplyEffects(r, g, b, brightness, vibrance, contrast, darkThreshold, darkFactor);

// Check if we need to apply fading
bool needsFade = hasPreviousFrame &&
fadeSpeed < 1.0 && // Only fade if fade speed is less than 1.0
!(lastFrameWasSolid &&
lastSolidR == processedColor.R &&
lastSolidG == processedColor.G &&
lastSolidB == processedColor.B);

if (needsFade)
{
// Calculate brightness values for current and previous frame
int prevBrightness = lastSolidR + lastSolidG + lastSolidB;
int newBrightness = processedColor.R + processedColor.G + processedColor.B;

// Determine if we're brightening or darkening
double fadeFactor = fadeSpeed; // Use the configured fade speed

// Apply transition
Parallel.For(0, width, i => {
resultBuffer[i] = FastBlendColors(previousFrame[i], processedColor, fadeFactor);
});
}
else
{
for (int i = 0; i < width; i++)
{
resultBuffer[i] = processedColor;
}
}

lastSolidR = processedColor.R;
lastSolidG = processedColor.G;
lastSolidB = processedColor.B;
}

[MethodImpl(MethodImplOptions.AggressiveOptimization)]
private void ExtractColumns(int stride, int width, int height, int bytesPerPixel)
{
Expand Down Expand Up @@ -258,7 +270,7 @@
int offset3 = (y + 2) * stride + columnOffset;
int offset4 = (y + 3) * stride + columnOffset;

totalB += (uint)pixelBuffer[offset1] + pixelBuffer[offset2] + pixelBuffer[offset3] + pixelBuffer[offset4];

Check warning on line 273 in KeyLighting/CPUImageProcessor.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
totalG += (uint)pixelBuffer[offset1 + 1] + pixelBuffer[offset2 + 1] + pixelBuffer[offset3 + 1] + pixelBuffer[offset4 + 1];
totalR += (uint)pixelBuffer[offset1 + 2] + pixelBuffer[offset2 + 2] + pixelBuffer[offset3 + 2] + pixelBuffer[offset4 + 2];
}
Expand All @@ -266,7 +278,7 @@
for (; y < height; y++)
{
int offset = y * stride + columnOffset;
totalB += pixelBuffer[offset];

Check warning on line 281 in KeyLighting/CPUImageProcessor.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
totalG += pixelBuffer[offset + 1];
totalR += pixelBuffer[offset + 2];
}
Expand Down
78 changes: 25 additions & 53 deletions KeyLighting/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class Program
static DateTime lastUpdate = DateTime.MinValue;
static DateTime lastDebugImageSave = DateTime.MinValue;

static float[] fadeProgress;
// Remove fade progress tracking
// static float[] fadeProgress;
static ORGBColor[] targetColors;

const int MIN_CAPTURE_INTERVAL_MS = 16;
Expand Down Expand Up @@ -74,7 +75,7 @@ static void Main(string[] args)

prevColors = new ORGBColor[ledCount];
ledColorsBuffer = new ORGBColor[ledCount];
fadeProgress = new float[ledCount];
// Removed fadeProgress
targetColors = new ORGBColor[ledCount];

var processor = new CPUImageProcessor(config);
Expand Down Expand Up @@ -216,6 +217,8 @@ static void ProcessFrame(ScreenCapturer capturer, CPUImageProcessor processor, O
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void UpdateLedColors(ORGBColor[] columnColors, LightingConfig config, int ledCount)
{
// Check if we want instant transitions (fadeSpeed at or very near 1.0)
bool instantTransition = config.FadeFactor >= 0.99;

var wasdEnabled = config.WASDEnabled;
var wasdKeys = wasdEnabled ? config.WASDKeys : Array.Empty<int>();
Expand All @@ -230,77 +233,46 @@ static void UpdateLedColors(ORGBColor[] columnColors, LightingConfig config, int
}

int columnLength = columnColors.Length;
float deltaTime = 1.0f / 60.0f;

for (int i = 0; i < ledCount; i++)
{
if (wasdEnabled && Array.IndexOf(wasdKeys, i) >= 0)
{

continue;
}
else
{

int columnIndex = Math.Min(i, columnLength - 1);
targetColors[i] = columnColors[columnIndex];
}
}

for (int i = 0; i < ledCount; i++)
{
if (wasdEnabled && Array.IndexOf(wasdKeys, i) >= 0)
{

// Handle WASD keys with special color
ledColorsBuffer[i] = new ORGBColor(wasdR, wasdG, wasdB);
prevColors[i] = ledColorsBuffer[i];
fadeProgress[i] = 1.0f;
}
else
{
// Apply column colors to the keyboard
int columnIndex = Math.Min(i, columnLength - 1);

ORGBColor prev = prevColors[i];
ORGBColor target = targetColors[i];

bool colorChanged =
Math.Abs(prev.R - target.R) > 3 ||
Math.Abs(prev.G - target.G) > 3 ||
Math.Abs(prev.B - target.B) > 3;

if (colorChanged && fadeProgress[i] >= 1.0f)
if (instantTransition)
{

fadeProgress[i] = 0.0f;
// With instantTransition, directly apply the column color
ledColorsBuffer[i] = columnColors[columnIndex];
}
else
{
// For backward compatibility, keep some very minimal smoothing
ORGBColor prev = prevColors[i];
ORGBColor target = columnColors[columnIndex];

fadeProgress[i] = (float)Math.Min(fadeProgress[i] + config.FadeFactor * deltaTime, 1.0f);

float t = EaseInOutCubic(fadeProgress[i]);

byte r = (byte)Math.Round(prev.R * (1 - t) + target.R * t);
byte g = (byte)Math.Round(prev.G * (1 - t) + target.G * t);
byte b = (byte)Math.Round(prev.B * (1 - t) + target.B * t);
// Simple lerp with very high weight toward target color
float t = 0.8f; // High value for quick transition but not instant

ledColorsBuffer[i] = new ORGBColor(r, g, b);
byte r = (byte)Math.Round(prev.R * (1 - t) + target.R * t);
byte g = (byte)Math.Round(prev.G * (1 - t) + target.G * t);
byte b = (byte)Math.Round(prev.B * (1 - t) + target.B * t);

if (fadeProgress[i] >= 1.0f)
{
prevColors[i] = target;
}
else
{
prevColors[i] = ledColorsBuffer[i];
ledColorsBuffer[i] = new ORGBColor(r, g, b);
}

// Store current color for next frame
prevColors[i] = ledColorsBuffer[i];
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static float EaseInOutCubic(float t)
{
return t < 0.5 ? 4 * t * t * t : 1 - (float)Math.Pow(-2 * t + 2, 3) / 2;
}

static void SaveDebugImages(Bitmap frame, ORGBColor[] columnColors, LightingConfig config)
{
try
Expand Down
2 changes: 1 addition & 1 deletion KeyLighting/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"brightnessMultiplier": 1.8,
"vibranceFactor": 1,
"contrastPower": 1.8,
"fadeFactor": 0.9,
"fadeFactor": 0.3,
"darkenThreshold": 65,
"darkenFactor": 0.4,
"wasdEnabled": false,
Expand Down
Loading