Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   BitMap zeichnen per API (https://www.delphipraxis.net/171831-bitmap-zeichnen-per-api.html)

sintronic86 27. Nov 2012 12:51

BitMap zeichnen per API
 
Hallo Community,

ich habe ein Problem, welches mich schon richtig frustriert.

Zur Vorgeschichte:
Ich habe ein Tool geschrieben, welches über eine ActiveX-Komponente Daten über die Ole-Schnittstelle
eines Bildverarbeitungsprogramms abgreift. Unter anderem erstellt dieses Programm auch Fotos über
angeschlossene Kameras, welche ich in meinem Tool anzeigen möchte.
Leider gibt das BV-Programm die Bilddaten nur im Typ OleVariant, hinter welchem sich der Typ Array of Array of Bytes verbirgt aus.

Diese Bytes "zu Fuß" auszuwerten und in ein BitMap zu zeichnen habe ich hinbekommen. Allerdings ist das ab einer
gewissen Kameraauflösung natürlich tierisch langsam.

Nun habe ich gehört, das es API-Funktionen gibt, mit denen das wesentlich schneller gehen soll.
Allerdings will es bei mir auf biegen und brechen nicht funktionieren!
Der häufigste Treffer meiner Suche war bisher immer die Funktion "SetDIBitsToDevice".
Aber ich weiss nicht, wie ich dort mein OleVariant unterbringen soll, geschweige wie die
Funktion im Allgemeinen fuktioniert.

Kann mir einer von euch da helfen?

nuclearping 27. Nov 2012 13:12

AW: BitMap zeichnen per API
 
Wie zeichnest du denn jetzt? Wenn du Pixel für Pixel in die Bitmap platzierst, könnte man das ggf. mit ScanLine optimieren.

Bei SetDIBitsToDevice kannst du (afaik) keinen OleVariant direkt unterbringen, sondern musst das Ganze noch in einen entsprechend verständlichen Pointer "casten" bzw. umkopieren.

Alternativ kann man vlt. statt SetDIBitsToDevice auch BitBlt nutzen.

Blup 27. Nov 2012 13:19

AW: BitMap zeichnen per API
 
Zitat:

Zitat von sintronic86 (Beitrag 1193216)
Diese Bytes "zu Fuß" auszuwerten und in ein BitMap zu zeichnen habe ich hinbekommen. Allerdings ist das ab einer
gewissen Kameraauflösung natürlich tierisch langsam.

Mit Scanline und Move sollte das aber nicht langsam sein, vieleicht zeigst du uns etwas Code.

SetDIBitsToDevice scheint hier nicht sinnvoll, wenn man die Daten davor umwandeln muss.

sintronic86 27. Nov 2012 13:28

AW: BitMap zeichnen per API
 
Hey, das ging ja schnell! :)


also bisher sieht es so aus (der wichtigste Ausschnitt der Funktion):


for I := BILDHÖHE - 1 downto 0 do
begin

TmpLine := BITMAP.ScanLine[I];

for J := 0 to BILDBREITE - 1 do
begin
try
TmpRGB := (OLEVARIANT[I,J] or (OLEVARIANT[I,J] shl 8) or (OLEVARIANT[I,J] shl 16));

TmpLine[J * 3 + 2] := TmpRGB;
TmpLine[J * 3 + 1] := TmpRGB;
TmpLine[J * 3] := TmpRGB;
finally

end;
end;
end;






Ja, die Idee mit Scanline hatte ich auch.
das hätte ich dann ungefähr so gelöst:


var TmpLine : PByteArray;
TmpALine: array of Byte;

for I := BILDHÖHE - 1 downto 0 do
begin

TmpLine := BITMAP.ScanLine[I];
TmpALine := OLEVARIANT[I];
Move(TmpALine, TmpLine, ???); <- wobei ich hier nicht genau weiss, was ich eintragen müsste

end;


Aber leider stehen die Einträge der ersten Dimension des Arrays für die Spalten
und die Einträge der zweiten für die Zeilen. Womit sich das auch erledigt hat oder?



Kannst du mir denn mal ein Beispiel für "BitBlt" mit meinem OleVariant geben?

Aphton 27. Nov 2012 14:32

AW: BitMap zeichnen per API
 
Du hast 3 Farbkanäle, die jeweils 1 Byte belegen, sprich 1 Farbpunkt belegt 3*1 = 3 Bytes.. Für jeden Farbpunkt pro Scanline musst du 3 Bytes kopieren. Dh. Breite * 3 kommt da rein.

Weiters ist mir persönlich aufgefallen, dass, wenn du die Scanlines ein einziges mal beim setzen der Dimensionen holst bzw zwischenpufferst, das ganze viel schneller läuft, als wenn du es jedes mal per Bitmap.Scanline[i] ne abfragst. Ich glaub der holt sich dann den jeweiligen Pointer per API Befehle, was unnötig ist!

sintronic86 27. Nov 2012 14:41

AW: BitMap zeichnen per API
 
@Aphton:
Ok, Breite mal 3: macht Sinn. Aber das würde doch an dem Problem nichts ändern, das ich nicht einfach sagen kann:

TmpALine := OLEVARIANT[I]

da ich damit ja die erste Spalte, statt Zeile anspreche.
Aber davon abgesehen, bekomme ich bei diesem Zugriff auch eine Fehlermeldung:

---------------------------
Benachrichtigung über Debugger-Exception
---------------------------
Im Projekt XXXXXXX.exe ist eine Exception der Klasse EVariantBadIndexError mit der Meldung 'Variante oder sicheres Array-Index außerhalb des gültigen Bereichs' aufgetreten.
---------------------------
Anhalten Fortsetzen Hilfe
---------------------------


Diese kann ich mir aber nicht erklären, da der Zugriff im selben Moment auf OLEVARIANT[I,0] einen
gültigen Wert herausgiebt! :(

Ich bin echt am verzweifeln. Habe jetzt ein wenig mit BitBlt herumgespielt, und bekomme es hin,
von einem Image in eine Paintbox zu kopieren. Allerdings wüsste ich nicht, wie ich dort mein OleVariant unterbringen kann.

Blup 27. Nov 2012 16:18

AW: BitMap zeichnen per API
 
Irgendwie ist der Code seltsam, warum läuft deine I-Schleife gegen Null?
Im Array ist zu jedem Pixel nur ein Byte aufgezeichnet (Graustufen vermutlich).
Du baust daraus ein TmpRGB, verwendest danach aber nur das unteren Byte von TmpRGB.
Dieses wird für jeden Farbkanal einzeln geschrieben (BGR, Pixelformat pf24Bit).

Das geht auch einfacher:
Delphi-Quellcode:
var
  TmpLine: PByte;
  TmpByte: Byte;

for I := 0 to BILDHÖHE - 1 do
begin
  TmpLine := BITMAP.ScanLine[I];

  for J := 0 to BILDBREITE - 1 do
  begin
    TmpByte := OLEVARIANT[I, J]; // Grau
    TmpLine^ := TmpByte; // Blau
    Inc(TmpLine);
    TmpLine^ := TmpByte; // Gruen
    Inc(TmpLine);
    TmpLine^ := TmpByte; // Rot
    Inc(TmpLine);
  end;
end;
Ist das noch nicht schnell genug, wirds komplizierter:
- Pixelformat der Bitmap auf 256Farben mit Palette umgestellt
- Palette mit 256 Graustufen erstellen und für die Bitmap auswählen
- mit SafeArrayLock() und SafeArrayAccessData() einen Pointer auf die Daten des Variant besorgen
- die Daten direkt in die Bitmap kopieren
- mit SafeArrayUnaccessData() und SafeArrayUnlock() den Variant wieder entsperren

nuclearping 27. Nov 2012 17:02

AW: BitMap zeichnen per API
 
Zitat:

Zitat von Blup (Beitrag 1193254)
Irgendwie ist der Code seltsam, warum läuft deine I-Schleife gegen Null?

Werden BMPs nicht kopfüber gespeichert?

Medium 27. Nov 2012 20:33

AW: BitMap zeichnen per API
 
Fast immer. Fast! Windows bot da irgend einen Weg heruaszufinden, wie es denn nun wirklich hinterlegt ist (ich weiss leider echt nicht mehr was das genau war, aber es stand im MSDN (nicht in den Kommentaren)), aber es gibt wohl ganz selten mal den Fall, dass dem nicht so ist. Ob und wann genau das eintritt stand dort leider nicht, und mir sind bisher auch noch keine "richtig-falsch-herumen" Bitmaps begegnet. Das aber nur am Rande :)

Blup 28. Nov 2012 08:21

AW: BitMap zeichnen per API
 
In welcher Reihenfolge die Bildzeilen im Speicher liegen, wird nicht dadurch beeinflusst, in welcher Reihenfolge ich diese kopiere.
Sowohl auf das Variantarray als auch auf ScanLine wird mit dem Index "I" zugegriffen.
Um das Bild vertikal zu spiegeln müsste genau einer der Zugriffe auf "BILDHÖHE - 1 - I" umgestellt werden.

ScanLine berücksichtigt bereits die Ausrichtung der Bilddaten im Speicher.
Scanline[0] ist immer die oberste Zeile, auch wenn diese im Speicher an letzter Stelle liegt.
Die Ole-Schnittstelle legt die Daten vermutlich in der richtigen Reihenfolge im Array ablegt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:28 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz