/// <summary> /// scanner worker thread /// </summary> protected void Scanner() { var bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb); var bmpResized = new Bitmap((int)(bmpScreenshot.Width * quality), (int)(bmpScreenshot.Height * quality)); uint w = (uint)bmpResized.Width, h = (uint)bmpResized.Height; // Create a graphics object from the bitmap. var gfxScreenshot = Graphics.FromImage(bmpScreenshot); uint framecount = 0; DateTime dtStart = DateTime.Now; int ttkey = 0; /*Time until next keyframe is captured*/ while (bRun) { Size sScreenshot = Screen.PrimaryScreen.Bounds.Size; if (ttkey == 0) { //Keyframe - take complete screenshot. gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, sScreenshot, CopyPixelOperation.SourceCopy); ttkey = 15; /*15 frames till next keyframe*/ } else { /* only snapshot center of screen*/ Size sCrop = new Size((int)(sScreenshot.Width * 0.25), (int)(sScreenshot.Height * 0.25)); sScreenshot.Height -= sCrop.Height; sScreenshot.Width -= sCrop.Width; // Take the screenshot from the upper left corner to the right bottom corner. gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X + sCrop.Width / 2, Screen.PrimaryScreen.Bounds.Y + sCrop.Height / 2, 0 + sCrop.Width / 2, 0 + sCrop.Height / 2, sScreenshot, CopyPixelOperation.SourceCopy); ttkey--; /*decrement time till next keyframe*/ } ResizeImage(bmpScreenshot, bmpResized); double r = 0, g = 0, b = 0, strongR = 0, strongG = 0, strongB = 0; UInt32 cPixels = 0, strongC = 0; double sumLuma = 0; /*Calculate average ARGB Values. * One could possibly improve performance of scanning pixels by directly access bitmap bytes * but as the biggest slice of scan time is lost during CopyFromScreen its disregarded for now*/ for (uint y = 0; y < h; y++) { uint numBlackPixelsInRow = 0;//if the entire row is black, this is propaby a black frame for (uint x = 0; x < w; x++) { ColorRGB clr = new ColorRGB(bmpResized.GetPixel((int)x, (int)y)); if (clr.L < 0.005) { numBlackPixelsInRow++; } //if (clr.L > 0.005 && clr.L < 0.95) /*Dont account for pitch black or plain white.*/ { cPixels++; r += clr.R; g += clr.G; b += clr.B; sumLuma += clr.L; if (clr.S > 0.3f) {/*This pixel is neiter black or white and so somwhat strongly saturated*/ strongR += clr.R; strongG += clr.G; strongB += clr.B; strongC++; } } } if (numBlackPixelsInRow == w) /*Entire row was black*/ { cPixels -= (uint)w; /*Kinda remove the black row.*/ } } r /= cPixels; g /= cPixels; b /= cPixels; if (strongC > 0) { strongR /= strongC; strongG /= strongC; strongB /= strongC; } lock (this) { avarageSaturatedColor = Color.FromArgb( 255, Math.Max(0, Math.Min(255, (int)strongR)), Math.Max(0, Math.Min(255, (int)strongG)), Math.Max(0, Math.Min(255, (int)strongB)) ); /*Current avarage screen colour becomes setpoint for lighting*/ avarageColor = Color.FromArgb( 255, Math.Max(0, Math.Min(255, (int)r)), Math.Max(0, Math.Min(255, (int)g)), Math.Max(0, Math.Min(255, (int)b)) ); avarageLuminuosity = (float)(sumLuma / cPixels); } System.Threading.Thread.Sleep(10); } }
// Given H,S,L,A in range of 0-1 // Returns a Color (RGB struct) in range of 0-255 public static ColorRGB FromHSLA(double H, double S, double L, double A) { double v; double r, g, b; if (A > 1.0) { A = 1.0; } r = L; // default to gray g = L; b = L; v = (L <= 0.5) ? (L * (1.0 + S)) : (L + S - L * S); if (v > 0) { double m; double sv; int sextant; double fract, vsf, mid1, mid2; m = L + L - v; sv = (v - m) / v; H *= 6.0; sextant = (int)H; fract = H - sextant; vsf = v * sv * fract; mid1 = m + vsf; mid2 = v - vsf; switch (sextant) { case 0: r = v; g = mid1; b = m; break; case 1: r = mid2; g = v; b = m; break; case 2: r = m; g = v; b = mid1; break; case 3: r = m; g = mid2; b = v; break; case 4: r = mid1; g = m; b = v; break; case 5: r = v; g = m; b = mid2; break; } } ColorRGB rgb = new ColorRGB(); rgb.R = Convert.ToByte(r * 255.0f); rgb.G = Convert.ToByte(g * 255.0f); rgb.B = Convert.ToByte(b * 255.0f); rgb.A = Convert.ToByte(A * 255.0f); return(rgb); }
protected void LightSetterProcess() { while (bRun) { System.Threading.Thread.Sleep(10); State setState; switch (Settings.scanMethod) { case ScanMethod.HueBoost: { /*methodb*/ ColorRGB huedColor = new ColorRGB(scanner.AvarageSaturatedColor); //ColorRGB totalColor = new ColorRGB(scanner.AvarageColor); float luma = scanner.AvarageLuma; setState = new State() { hue = (ushort)(huedColor.H * ushort.MaxValue), sat = (byte)(huedColor.S * 255), bri = (byte)(luma * 255), on = true, transitiontime = Settings.transitiontime }; break; } case ScanMethod.Avarage: { //total avarages ColorRGB c = new ColorRGB(scanner.AvarageColor); setState = new State() { hue = (ushort)(c.H * ushort.MaxValue), sat = (byte)(c.S * 255), bri = (byte)(c.L * 255), on = true, transitiontime = Settings.transitiontime }; break; } case ScanMethod.LumaOnly: setState = new State() { bri = (byte)(scanner.AvarageLuma * 255), on = true, transitiontime = Settings.transitiontime }; break; default: { throw new Exception("Unexpected scanmode in settings"); } } /*Do luma boost/reduction and limit min max light levels*/ setState.bri = (byte)Math.Max(Settings.minLight, Math.Min((float)setState.bri * Settings.luminuosity, Settings.maxLight)); /*Do sat boost/reduction*/ if (Settings.saturation != 1) { setState.sat = (byte)Math.Max(0, Math.Min((float)setState.sat * Settings.saturation, 255)); } /*Decide if change shall be send to the bridge*/ bool bSendUpdate = false; if (lastState == null) /*Send update if none sent yet*/ { bSendUpdate = true; } else if (DateTime.Now.Second % 5 == 0) /*Force send update every 5 seconds*/ { bSendUpdate = true; } else /*Decide if scene changes are big enough to send an update*/ { float delta = 0; delta += Math.Abs(((float)lastState.bri - (float)setState.bri) / Byte.MaxValue); delta += Math.Abs(((float)lastState.hue - (float)setState.hue) / ushort.MaxValue); delta += Math.Abs(((float)lastState.sat - (float)setState.sat) / Byte.MaxValue); delta /= 3; if (delta >= Settings.minDelta) { bSendUpdate = true; } } if (bSendUpdate) { lastState = setState; foreach (int lid in Settings.lights) { bridge.SetLightState(lid.ToString(), setState); } foreach (int gid in Settings.groups) { HueLib_base.Action a = new HueLib_base.Action(); a.on = true; a.sat = setState.sat; a.hue = setState.hue; a.bri = setState.bri; bridge.SetGroupAction(gid.ToString(), a); } lock (this) lightUpdatesSent++; } } }