Wie geht das (C#)?
Wie kann ich vom Bildschirm (alle Bildschirme zusammen) alle Pixel also die Position (x und y) und die Farbe in ein 2d Array speichern um sie dann in ein Bitmap einzuzeichnen? Also nicht wie ich sie in ein Array speichere oder in ein Bitmap zeichne sondern wie ich die Informationen bekomme
3 Antworten
So einfach wie es sich meine Vorredener gemacht haben ist die Nummer leider nicht
Du willst alle Monitore erfassen ist zwar relativ billig, aber physische Tücken, wenn der Hauptmonitor in der Mitte oder rechts ist:
var vScreen = SystemInformation.VirtualScreen;
die Linke obere Ecke kann sich unterhalb 0 befinden:
meine Konfig:
vScreen Properties:
Location : {X=-1920,Y=0}
Size : {Width=5520, Height=1080}
X : -1920
Y : 0
Width : 5520
Height : 1080
Left : -1920
Top : 0
Right : 3600
Bottom : 1080
IsEmpty : False
...das wiederum dürfte wohl Lookbits nicht unbedingt schmecken. Arrays mit negativem Index?. 😱
Erstmal einen ganz banalen Screenshot, der hat dann auch als Basis 0,0
Den kann man schön übersichtlich zerlegen, denn das dauert wertvolle Millisekunden, welche einem laufenden Screen fehlen könnten und Lookbits sperrt nunmal den Zugriff auf eine Datenquelle für andere Threads. (den Screen) ein NoGo!!!
Ein Screenshot sind 5 Zeilen Code... ein wahrlich großes Opfer.😉
Aus den Bitmap (Screenshot) dann einen eine 2DArray zu bauen ist banal.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
class Prog{
private static Bitmap Screenshot(){
//äußerste Begrenzung aller aktiven Screens ermitteln
var vScreen = SystemInformation.VirtualScreen;
//neues Bitmap mit der errechneten Größe bereitstellen
Bitmap bmpScreenshot = new Bitmap(vScreen.Width, vScreen.Height);
// die gesamte berechnete Screenfläche in unser neues Bitmap übertragen
//(x und Y im Ziel sind 0)
Graphics g = Graphics.FromImage(bmpScreenshot);
g.CopyFromScreen(vScreen.X,vScreen.Y, 0, 0, vScreen.Size);
return bmpScreenshot;
}
public static Color[,] ImageTo2DByteArray(Bitmap bmp){
int width = bmp.Width;
int height = bmp.Height;
//Rohe 32bit-Farbdaten ermitteln in RGB-ByteArray speichern
BitmapData data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
//einfach nur Bytes ohne Ende
byte[] bytes = new byte[height * data.Stride];
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
bmp.UnlockBits(data);
//plump unser lineares Array per x-y-Loops in ein 2D_Array of Color umverteilen
//...wir haben nur einen lange Reihe Bytes, von den jeweils 4 zusammengehören
//...die müssen wir schön umverteilen
Color[,] result = new Color[height, width];
int currentRowOffs = 0;
for (int y = 0; y < bmp.Height; y++){
// Startoffset der aktuellen Spalte
int currentOffs = currentRowOffs;
for (int x = 0; x < bmp.Width; x++){
// ARGB = bytes [B,G,R,A] , !!!Byteorder auf x86-kompatiblen!!!
Byte b = bytes[currentOffs];
Byte g = bytes[currentOffs + 1];
Byte r = bytes[currentOffs + 2];
Byte a = bytes[currentOffs + 3];
Color col = Color.FromArgb(a, r, g, b);
//aktuellen Offset im linearen Array "bytes" um 4 erhöhen
currentOffs += 4;
result[y,x] = col;
}
// ganze Zeile Brite*4Bytes weiter
currentRowOffs += data.Stride;
}
return result;
}
public static void Main(string[] args){
//einfach einen billigen Screnshot von allen Bildschirmen machen
Bitmap screenCopy = Screenshot();
screenCopy.Save("test.bmp",ImageFormat.Bmp); //mal speichern damit was sieht
var my_2D_RGBarray = ImageTo2DByteArray(screenCopy);
Console.WriteLine("Drücke J wenn die X_Y-Koortinaten und deren farbcode angezeigt werden sollen");
var keyInfo = Console.ReadKey(true);
if (keyInfo.Key.ToString() == "J"){
// mal jede einzelne Punktinformation des Bildes anzeigen.
//Kannst Du wenn Du Bock hast, auch so in eine Datei schreiben
//kann Dauern 😴
for(int y = 0; y < my_2D_RGBarray.GetLength(0); y++)
for(int x = 0; x < my_2D_RGBarray.GetLength(1); x++){
Color color = my_2D_RGBarray[y,x];
String HtmlColorCode = string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", color.A, color.R, color.G, color.B);
Console.WriteLine("X={0} Y={1} Colorcode={2} {3}", x, y, HtmlColorCode, my_2D_RGBarray[y,x]) ;
}
}
Console.WriteLine("fertich...");
Console.ReadKey();
}
}
ich habe drauf verzichtet das Fenster auch noch auszublenden ist ne Demo aus der Hüfte, zum weiterbasteln
Crossplattform bei einer Handvoll Zeilen?
- https://github.com/mono/SkiaSharp
- https://dotnetfoundation.org/
- https://blog.aspose.com/drawing/using-system-drawing-in-linux/
- https://swharden.com/csdv/platforms/compare/
Im übrigen kann es bei solchem Minicode nicht Problem sein um über native APIs entsprechende Funktionen zu implementieren.
Eine all-in Bibliothek, welche jedes System befriedigt wirst Du wohl kaum finden.
Gegebenenfalls musst Du eben darüber nachdenken das Projekt in Java (Swing) anzufassen.
Eine Programmiersprache ist ein Werkzeug. Und man nutzt jenes, welches am besten passt.
(die Demo hätte ich ebenso in Powershell, Java, Delphi, Python ... schreiben können. -hätte lediglich etwas Recherche bezüglich nötiger Bibliotheken gekostet)
Du kannst dir mit Hilfe der Windows API (BitBlt) die Farbe eines Pixels in ein Bild kopieren.
using (var destination = Graphics.FromImage(new Bitmap(1, 1, PixelFormat.Format32bppArgb)))
{
using (var source = Graphics.FromHwnd(IntPtr.Zero))
{
var hdcSrc = source.GetHdc();
var hdc = destination.GetHdc();
var result = BitBlt(hdc, 0, 0, 1, 1, hdcSrc, x, y, (int)CopyPixelOperation.SourceCopy);
destination.ReleaseHdc();
source.ReleaseHdc();
}
}
Die aktuellen Bildschirmkoordinaten könntest du via GetCursorPos erfragen.
Falls du noch nie Funktionen der Windows API in einem C#-Projekt eingebunden hast, findest du hier einen kurzen Artikel dazu.
mit Graphics.FromHwnd erwischt man leider nicht mehrere Monitore...
inspiziere mal (quick&dirty) das von Graphics.FromHwnd(IntPtr.Zero) zurückgelieferte Object. in der Powershell:
PS C:\Users\Erzesel Secure\Desktop> Add-Type -a System.Drawing
PS C:\Users\Erzesel Secure\Desktop> [Drawing.Graphics]::FromHwnd([IntPtr]::Zero)
CompositingMode : SourceOver
RenderingOrigin : {X=0,Y=0}
CompositingQuality : Default
TextRenderingHint : SystemDefault
TextContrast : 4
SmoothingMode : None
PixelOffsetMode : Default
InterpolationMode : Bilinear
Transform : System.Drawing.Drawing2D.Matrix
PageUnit : Display
PageScale : 1
DpiX : 96
DpiY : 96
Clip : System.Drawing.Region
ClipBounds : {X=-4194304,Y=-4194304,Width=8388608,Height=8388608}
IsClipEmpty : False
VisibleClipBounds : {X=0,Y=0,Width=1920,Height=1080}
IsVisibleClipEmpty : False
Die erfassbaren Grenzen beschränken sich allen auf den Hauptmonitor (VisibleClipBounds) .
Ein MultiomonitorDesktop arbeitet mit einem Virtuellen Screen, welcher die Grenzen aller Displays hat. (System.Windows.Forms.Screen.AllScreens)
Du könntest die Graphics-Klasse verwenden, um auf die Bildschirme zuzugreifen, und dann mit einer Schleife über die Pixel iterieren, um Positionen und Farben auszulesen.
Achso, sollte vorhanden sein.
Bin erst Abend wieder am Rechner zum schauen.
gehört auch zum System.Drawing.
Wenn du sie nicht finden kannst, schau nach, ob die System.Drawing Bibliothek richtig eingebunden ist. Du kannst using System.Drawing; am Anfang deiner Datei hinzufügen, um sie zu verwenden. Aber denke mal das weißt du von alleine :-).
ich schaue Abend nochmal nach.
Graphics ist ab NET 6 ausschließlich für Windows OS verfügbar. In cross-platform Projekten kannst du daher nicht auf diesen Typ zurückgreifen. Weiteres (Assembly, Namespace, etc.) steht in der Dokumentation.
Ich kann die System.Drawing.Graphics Klasse nicht benutzen, weil alles crossplatform laufen soll und es unterstützt nur windows