![]() |
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? |
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. |
AW: BitMap zeichnen per API
Zitat:
SetDIBitsToDevice scheint hier nicht sinnvoll, wenn man die Daten davor umwandeln muss. |
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? |
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! |
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. |
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:
Ist das noch nicht schnell genug, wirds komplizierter:
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; - 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 |
AW: BitMap zeichnen per API
Zitat:
|
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 :)
|
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 09:55 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz