diff --git a/app/Gpu/GPUModeControl.cs b/app/Gpu/GPUModeControl.cs index 831bbf72..075e0051 100644 --- a/app/Gpu/GPUModeControl.cs +++ b/app/Gpu/GPUModeControl.cs @@ -54,7 +54,7 @@ namespace GHelper.Gpu AppConfig.Set("gpu_mode", gpuMode); settings.VisualiseGPUMode(gpuMode); - Aura.ApplyGPUColor(); + Aura.CustomRGB.ApplyGPUColor(); } diff --git a/app/Helpers/ColorUtilities.cs b/app/Helpers/ColorUtilities.cs index f6e8db78..1a21316d 100644 --- a/app/Helpers/ColorUtilities.cs +++ b/app/Helpers/ColorUtilities.cs @@ -1,6 +1,8 @@ -namespace GHelper.Helpers +using System.Runtime.CompilerServices; + +namespace GHelper.Helpers { - public class ColorUtilities + public class ColorUtils { // Method to get the weighted average between two colors public static Color GetWeightedAverage(Color color1, Color color2, float weight) @@ -16,6 +18,144 @@ return Color.FromArgb(red, green, blue); } + + public static Color GetMidColor(Color color1, Color color2) + { + return Color.FromArgb((color1.R + color2.R) / 2, + (color1.G + color2.G) / 2, + (color1.B + color2.B) / 2); + } + + public class HSV + { + public double Hue { get; set; } + public double Saturation { get; set; } + public double Value { get; set; } + + public Color ToRGB() + { + var hue = Hue * 6; + var saturation = Saturation; + var value = Value; + + double red; + double green; + double blue; + + if (saturation == 0) + { + red = green = blue = value; + } + else + { + var i = Convert.ToInt32(Math.Floor(hue)); + var f = hue - i; + var p = value * (1 - saturation); + var q = value * (1 - saturation * f); + var t = value * (1 - saturation * (1 - f)); + int mod = i % 6; + + red = new[] { value, q, p, p, t, value }[mod]; + green = new[] { t, value, value, q, p, p }[mod]; + blue = new[] { p, p, t, value, value, q }[mod]; + } + + return Color.FromArgb(Convert.ToInt32(red * 255), Convert.ToInt32(green * 255), Convert.ToInt32(blue * 255)); + } + + public static HSV ToHSV(Color rgb) + { + double red = rgb.R / 255.0; + double green = rgb.G / 255.0; + double blue = rgb.B / 255.0; + var min = Math.Min(red, Math.Min(green, blue)); + var max = Math.Max(red, Math.Max(green, blue)); + var delta = max - min; + double hue; + double saturation = 0; + var value = max; + + if (max != 0) + saturation = delta / max; + + if (delta == 0) + hue = 0; + else + { + if (red == max) + hue = (green - blue) / delta + (green < blue ? 6 : 0); + else if (green == max) + hue = 2 + (blue - red) / delta; + else + hue = 4 + (red - green) / delta; + + hue /= 6; + } + + return new HSV { Hue = hue, Saturation = saturation, Value = value }; + } + + public static Color UpSaturation(Color rgb, float increse = 0.2f) //make color more colored + { + if (rgb.R == rgb.G && rgb.G == rgb.B) + return rgb; + var hsv_color = ToHSV(rgb); + hsv_color.Saturation = Math.Min(hsv_color.Saturation + increse, 1.00f); + return hsv_color.ToRGB(); + } + + } + + public class SmoothColor + { + public Color RGB + { + get { return Interpolate(); } + set { clr = value; } + } + + Color Interpolate() + { + clr_ = ColorInterpolator.InterpolateBetween(clr, clr_, smooth); + return clr_; + } + + private float smooth = 0.65f; //smooth + private Color clr = new Color(); + private Color clr_ = new Color(); + + static class ColorInterpolator + { + delegate byte ComponentSelector(Color color); + static ComponentSelector _redSelector = color => color.R; + static ComponentSelector _greenSelector = color => color.G; + static ComponentSelector _blueSelector = color => color.B; + + public static Color InterpolateBetween(Color endPoint1, Color endPoint2, double lambda) + { + if (lambda < 0 || lambda > 1) + throw new ArgumentOutOfRangeException("lambda"); + + if (endPoint1 != endPoint2) + { + return Color.FromArgb( + InterpolateComponent(endPoint1, endPoint2, lambda, _redSelector), + InterpolateComponent(endPoint1, endPoint2, lambda, _greenSelector), + InterpolateComponent(endPoint1, endPoint2, lambda, _blueSelector) + ); + } + + return endPoint1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static byte InterpolateComponent(Color end1, Color end2, double lambda, ComponentSelector selector) + { + return (byte)(selector(end1) + (selector(end2) - selector(end1)) * lambda); + } + } + + } } } \ No newline at end of file diff --git a/app/USB/Aura.cs b/app/USB/Aura.cs index 5e755a7f..5e4fa18d 100644 --- a/app/USB/Aura.cs +++ b/app/USB/Aura.cs @@ -1,5 +1,11 @@ using GHelper.Gpu; using GHelper.Helpers; +using GHelper.Input; +using Microsoft.VisualBasic.Devices; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; using System.Text; namespace GHelper.USB @@ -47,6 +53,7 @@ namespace GHelper.USB Flash = 12, HEATMAP = 20, GPUMODE = 21, + AMBIENT = 22, } public enum AuraSpeed : int @@ -63,6 +70,8 @@ namespace GHelper.USB static byte[] MESSAGE_APPLY = { AsusHid.AURA_ID, 0xb4 }; static byte[] MESSAGE_SET = { AsusHid.AURA_ID, 0xb5, 0, 0, 0 }; + static readonly int AURA_ZONES = 0x12; + private static AuraMode mode = AuraMode.AuraStatic; private static AuraSpeed speed = AuraSpeed.Normal; @@ -111,6 +120,7 @@ namespace GHelper.USB { AuraMode.Comet, "Comet" }, { AuraMode.Flash, "Flash" }, { AuraMode.HEATMAP, "Heatmap"}, + { AuraMode.AMBIENT, "Ambient"}, }; static Aura() @@ -199,23 +209,17 @@ namespace GHelper.USB private static void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e) { - SetHeatmap(); - } + if (!InputDispatcher.backlightActivity) + return; - static void SetHeatmap(bool init = false) + if (Mode == AuraMode.HEATMAP) { - float cpuTemp = (float)HardwareControl.GetCPUTemp(); - int freeze = 20, cold = 40, warm = 65, hot = 90; - Color color; - - //Debug.WriteLine(cpuTemp); - - if (cpuTemp < cold) color = ColorUtilities.GetWeightedAverage(Color.Blue, Color.Green, ((float)cpuTemp - freeze) / (cold - freeze)); - else if (cpuTemp < warm) color = ColorUtilities.GetWeightedAverage(Color.Green, Color.Yellow, ((float)cpuTemp - cold) / (warm - cold)); - else if (cpuTemp < hot) color = ColorUtilities.GetWeightedAverage(Color.Yellow, Color.Red, ((float)cpuTemp - warm) / (hot - warm)); - else color = Color.Red; - - ApplyColor(color, init); + CustomRGB.ApplyHeatmap(); + } + else if (Mode == AuraMode.AMBIENT) + { + CustomRGB.ApplyAmbient(); + } } @@ -356,12 +360,17 @@ namespace GHelper.USB } - public static void ApplyColor(Color color, bool init = false) + public static void ApplyColor(Color color, bool init = false) { + Color[] color_list = Enumerable.Repeat(color, 0x12).ToArray(); + ApplyColor(color_list, init); + } + + public static void ApplyColor(Color[] color, bool init = false) { if (isACPI) { - Program.acpi.TUFKeyboardRGB(0, color, 0, null); + Program.acpi.TUFKeyboardRGB(0, color[0], 0, null); return; } @@ -381,11 +390,11 @@ namespace GHelper.USB msg[6] = 0; msg[7] = 0x10; - for (byte i = 0; i < 0x12; i++) + for (byte i = 0; i < AURA_ZONES; i++) { - msg[start + i * 3] = color.R; // R - msg[start + 1 + i * 3] = color.G; // G - msg[start + 2 + i * 3] = color.B; // B + msg[start + i * 3] = color[i].R; // R + msg[start + 1 + i * 3] = color[i].G; // G + msg[start + 2 + i * 3] = color[i].B; // B } if (init) @@ -412,32 +421,12 @@ namespace GHelper.USB else { - AsusHid.WriteAura(AuraMessage(0, color, color, 0)); + AsusHid.WriteAura(AuraMessage(0, color[0], color[0], 0)); AsusHid.WriteAura(MESSAGE_SET); } } - - public static void ApplyGPUColor() - { - if ((AuraMode)AppConfig.Get("aura_mode") != AuraMode.GPUMODE) return; - - switch (GPUModeControl.gpuMode) - { - case AsusACPI.GPUModeUltimate: - ApplyColor(Color.Red, true); - break; - case AsusACPI.GPUModeEco: - ApplyColor(Color.Green, true); - break; - default: - ApplyColor(Color.Yellow, true); - break; - } - } - - public static void ApplyAura() { @@ -450,14 +439,23 @@ namespace GHelper.USB if (Mode == AuraMode.HEATMAP) { - SetHeatmap(true); + CustomRGB.ApplyHeatmap(true); timer.Enabled = true; + timer.Interval = 2000; + return; + } + + if (Mode == AuraMode.AMBIENT) + { + CustomRGB.ApplyAmbient(true); + timer.Enabled = true; + timer.Interval = 80; return; } if (Mode == AuraMode.GPUMODE) { - ApplyGPUColor(); + CustomRGB.ApplyGPUColor(); return; } @@ -471,6 +469,189 @@ namespace GHelper.USB } + public static class CustomRGB { + + public static void ApplyGPUColor() + { + if ((AuraMode)AppConfig.Get("aura_mode") != AuraMode.GPUMODE) return; + + switch (GPUModeControl.gpuMode) + { + case AsusACPI.GPUModeUltimate: + ApplyColor(Color.Red, true); + break; + case AsusACPI.GPUModeEco: + ApplyColor(Color.Green, true); + break; + default: + ApplyColor(Color.Yellow, true); + break; + } + } + + public static void ApplyHeatmap(bool init = false) + { + float cpuTemp = (float)HardwareControl.GetCPUTemp(); + int freeze = 20, cold = 40, warm = 65, hot = 90; + Color color; + + //Debug.WriteLine(cpuTemp); + + if (cpuTemp < cold) color = ColorUtils.GetWeightedAverage(Color.Blue, Color.Green, ((float)cpuTemp - freeze) / (cold - freeze)); + else if (cpuTemp < warm) color = ColorUtils.GetWeightedAverage(Color.Green, Color.Yellow, ((float)cpuTemp - cold) / (warm - cold)); + else if (cpuTemp < hot) color = ColorUtils.GetWeightedAverage(Color.Yellow, Color.Red, ((float)cpuTemp - warm) / (hot - warm)); + else color = Color.Red; + + ApplyColor(color, init); + } + + public static void ApplyAmbient(bool init = false) + { + var bound = Screen.GetBounds(Point.Empty); + bound.Y += bound.Height / 3; + bound.Height -= (int)Math.Round(bound.Height * (0.33f + 0.022f)); // cut 1/3 of the top screen + windows panel + + var screen_low = AmbientData.CamptureScreen(bound, 512, 288); + Bitmap screeb_pxl = AmbientData.ResizeImage(screen_low, 4, 2); // 4x2 zone. top for keyboard and bot for lightbar + + if (isStrix) //laptop with lightbar + { + var mid_left = ColorUtils.GetMidColor(screeb_pxl.GetPixel(0, 1), screeb_pxl.GetPixel(1, 1)); + var mid_right = ColorUtils.GetMidColor(screeb_pxl.GetPixel(2, 1), screeb_pxl.GetPixel(3, 1)); + + AmbientData.Colors[6].RGB = ColorUtils.HSV.UpSaturation(screeb_pxl.GetPixel(3, 1)); // right bck + AmbientData.Colors[11].RGB = ColorUtils.HSV.UpSaturation(screeb_pxl.GetPixel(1, 1)); // left bck + + AmbientData.Colors[7].RGB = AmbientData.Colors[6].RGB; // right + AmbientData.Colors[10].RGB = AmbientData.Colors[11].RGB; // left + + AmbientData.Colors[8].RGB = ColorUtils.HSV.UpSaturation(mid_right); // center right + AmbientData.Colors[9].RGB = ColorUtils.HSV.UpSaturation(mid_left); // center left + + for (int i = 0; i < 4; i++) // keyboard + AmbientData.Colors[i].RGB = ColorUtils.HSV.UpSaturation(screeb_pxl.GetPixel(i, 0)); + } + else + { + for (int i = 0; i < 4; i++) //just color transfer from the bottom screen on keyboard + AmbientData.Colors[i].RGB = ColorUtils.HSV.UpSaturation(screeb_pxl.GetPixel(i, 1)); + } + + + //screeb_pxl.Save("test.jpg", ImageFormat.Jpeg); + screen_low.Dispose(); + screeb_pxl.Dispose(); + + bool is_fresh = false; + + for (int i = 0; i < AURA_ZONES; i++) + { + if (AmbientData.result[i].ToArgb() != AmbientData.Colors[i].RGB.ToArgb()) + is_fresh = true; + AmbientData.result[i] = AmbientData.Colors[i].RGB; + } + + if (is_fresh) + ApplyColor(AmbientData.result, init); + } + + static class AmbientData + { + + public enum StretchMode + { + STRETCH_ANDSCANS = 1, + STRETCH_ORSCANS = 2, + STRETCH_DELETESCANS = 3, + STRETCH_HALFTONE = 4, + } + + [DllImport("user32.dll")] + private static extern IntPtr GetDesktopWindow(); + + [DllImport("user32.dll")] + private static extern IntPtr GetWindowDC(IntPtr hWnd); + + [DllImport("gdi32.dll")] + private static extern IntPtr CreateCompatibleDC(IntPtr hDC); + + [DllImport("gdi32.dll")] + private static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight); + + [DllImport("gdi32.dll")] + private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); + + [DllImport("user32.dll")] + private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC); + + [DllImport("gdi32.dll")] + private static extern bool DeleteDC(IntPtr hdc); + + [DllImport("gdi32.dll")] + private static extern bool DeleteObject(IntPtr hObject); + + [DllImport("gdi32.dll")] + private static extern bool StretchBlt(IntPtr hdcDest, int nXOriginDest, int nYOriginDest, + int nWidthDest, int nHeightDest, + IntPtr hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, Int32 dwRop); + + [DllImport("gdi32.dll")] + static extern bool SetStretchBltMode(IntPtr hdc, StretchMode iStretchMode); + + /// + /// Captures a screenshot. + /// + public static Bitmap CamptureScreen(Rectangle rec, int out_w, int out_h) + { + IntPtr desktop = GetDesktopWindow(); + IntPtr hdc = GetWindowDC(desktop); + IntPtr hdcMem = CreateCompatibleDC(hdc); + + IntPtr hBitmap = CreateCompatibleBitmap(hdc, out_w, out_h); + IntPtr hOld = SelectObject(hdcMem, hBitmap); + SetStretchBltMode(hdcMem, StretchMode.STRETCH_DELETESCANS); + StretchBlt(hdcMem, 0, 0, out_w, out_h, hdc, rec.X, rec.Y, rec.Width, rec.Height, 0x00CC0020); + SelectObject(hdcMem, hOld); + + DeleteDC(hdcMem); + ReleaseDC(desktop, hdc); + var result = Image.FromHbitmap(hBitmap, IntPtr.Zero); + DeleteObject(hBitmap); + return result; + } + + public static Bitmap ResizeImage(Image image, int width, int height) + { + var destRect = new Rectangle(0, 0, width, height); + var destImage = new Bitmap(width, height); + + destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); + + using (var graphics = Graphics.FromImage(destImage)) + { + graphics.CompositingMode = CompositingMode.SourceCopy; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.Bicubic; + graphics.SmoothingMode = SmoothingMode.None; + graphics.PixelOffsetMode = PixelOffsetMode.None; + + using (var wrapMode = new ImageAttributes()) + { + wrapMode.SetWrapMode(WrapMode.TileFlipXY); + graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode); + } + } + + return destImage; + } + + static public Color[] result = new Color[AURA_ZONES]; + static public ColorUtils.SmoothColor[] Colors = Enumerable.Repeat(0, AURA_ZONES). + Select(h => new ColorUtils.SmoothColor()).ToArray(); + } + + } + } } \ No newline at end of file