Einführung
Wie ist eine DIB aufgebaut
RGB Konvertierungen
Wie dreht man eine Bitmap um 90°
Wandlung in Graustufen und Ändern der Palette
Wie kopiere ich ein Byte-Array in eine Bitmap (ab Delphi 3)
Wie benutze ich ein JPEG (ab Delphi 3)
Wie kann ich eine Vorschau der JPEG-Kompression machen (ab Delphi 3)
Bei den meisten der hier beschriebenen Lösungen werden DIB's benutzt.
Ein DIB ist ein geräteunabhängiges Bitmap.
Um ein TBitmap in ein DIB zu verwandeln, kann TBitmap.SaveToStream benutzt werden.
Zurück geht es dann mit TBitmap.LoadFromStream .
Seit Delphi 3 wird es einfacher, hier kann man auch direkt auf die ScanLines zugreifen.
Ich gehe hier nur auf unkomprimierte Bitmap mit einer Ebene ein, da diese den "Standard"-Fall darstellen.
Die Scanlines sind am Ende auf 4 Byte ausgerichtet, deshalb wird die Größe einer Scanline mit WidthBytes berechnet.
Bei Monochromen Bitmaps sind es nur 2 Byte.
Die erste Scanline enthält die unterste Zeile einer Bitmap.
Die Pixel werden wie folgt dargestellt:
Bits | Farben | 1 Pixel | Pixel-Beschreibung (nicht erwähnte Bits/Bytes sind Null) |
1 | 2 | 1Bit | Palettenindex |
4 | 16 | 4Bit | Palettenindex |
8 | 256 | 8Bit | Palettenindex |
15 | 32768 | 16Bit | Bit14..10:Rot (5Bit), Bit9..5:Grün (5Bit), Bit4..0:Blau (5Bit) |
16 | 65536 | 16Bit | Bit15..11:Rot (5Bit), Bit10..6:Grün (5Bit), Bit5..0:Blau (6Bit) |
24 | 16777216 | 16Bit | Bit23..16 (Byte2):Rot, Bit15..8 (Byte1):Grün, Bit7..0 (Byte0):Blau |
32 | 16777216 | 32Bit | Bit23..16 (Byte2):Rot, Bit15..8 (Byte1):Grün, Bit7..0 (Byte0):Blau |
Werde ich später noch genauer ausführen, jetzt erstmal nur der Verweis auf die Online-Hilfe.
Delphi 1: WinAPI.hlp (in deutsch außer Graphics File Formats)
Themen: TBitmapCoreHeader, TBitmapCoreInfo, TBitmapFileHeader, TBitmapInfo, TBitmapInfoHeader, Graphics File Formats
Delphi 2/3: MAPI.hlp (nur in englisch)
Themen: BitmapCoreHeader, BitmapCoreInfo,
BitmapFileHeader, BitmapInfo, BitmapInfoHeader
Frage:
Wie kann ich aus der Angabe der R-, G- und B-Anteile eines Pixels den dazugehörigen Wert für TColor errechnen?
Antwort:
Mit der Funktion RGB.
Weitere Funktionen stehen in Windows.pas bereit:
Funktion | Umsetzung |
function RGB(r, g, b: Byte): COLORREF; | Result := (r or (g shl 8) or (b shl 16)); |
function PaletteRGB(r, g, b: Byte): COLORREF; | Result := $02000000 or RGB(r,g,b); |
function PaletteIndex(i: Word): COLORREF; | Result := $01000000 or i; |
function GetRValue(rgb: DWORD): Byte; | Result := Byte(rgb); |
function GetGValue(rgb: DWORD): Byte; | Result := Byte(rgb shr 8); |
function GetBValue(rgb: DWORD): Byte; | Result := Byte(rgb shr 16); |
Dazu habe ich ein kleines Demo geschrieben, daß 256-Farben und TrueColor Bitmaps um 90° (in beide Richtungen) dreht. Die Unit Bmp90D12 enthält dabei die Umsetzung für Delphi 1 und 2, die Unit Bmp90D34 die Umsetzung für Delphi 3 und 4. Download (6 kByte) (überarbeitet am 28.12.98)
Die Routinen WidthBytes und GetDInColors sowie bei Delphi 1 die Routine OffsetPointer entstammen den Original VCL-Sourcen von Graphics und Classes.
Das Demo ist überarbeitet, läuft jetzt auch unter Delphi 2. Zusätzlich ist ein Fehler behoben, der bei von 8Bit nach 24Bit konvertierten Bitmaps auftritt.
Dazu habe ich ein kleines Demo geschrieben, daß 8, 24 und 32 Bit-Bitmaps in ein Graustufenbild wandelt und in einem zweiten Schritt die Palette dieser Bitmap ändert. Die Unit BmpGrD12 enthält dabei die Umsetzung für Delphi 1 und 2, das Demo läuft aber auch unter Delphi 3 und 4. Die Umsetzung für Delphi 1 ist unvollständig, Bitmaps > 64kByte werden falsch bearbeitet. Das werde ich zu einem späteren Zeitpunkt noch mal überarbeiten. Download (6 kByte) (erstellt am 02.03.99).
Problem:
Ich habe ein Byte-Array mit den Pixeln und eines mit den Paletteneinträgen, wie erzeuge ich daraus eine Bitmap ?
Arraydefinitionen:
Pal : array[0..255,0.. 2] of Byte; Pixels : array[0..255,0..19] of Byte; |
Lösung für Delphi 3:
Du brauchst ein Formular mit einem Button und zwei TImage mit AutoSize=True.
Es gibt 2 verschiedene Methoden, die erste arbeitet über eine 8 Bit-Bitmap und Palette, die zweite über eine 32 Bit-Bitmap.
Bei der ersten Methode bin ich mir nicht sicher, ob durch das CreatePalette nicht ein Ressourcenleck entsteht. Normalerweise muß noch ein DeleteObject(hPal) kommen, das würde jedoch die benutzte Palette zerstören.
Zuerst noch eine Hilfsfunktion, die die Funktion RGB für Scanlines umsetzt, da dort die Reihenfolge genau umgekehrt ist: B,G,R,0 (sonst R,G,B).
function ScanLineRGB32Bit(R,G,B:Byte):LongInt; begin Result:=(R shl 16) or (G shl 8) or B; end; |
Jetzt die eigentliche Funktion:
Zum Schluß noch die Definitionen der Paletteneinträge
(aus der Unit windows):
PPaletteEntry = ^TPaletteEntry; TPaletteEntry = packed record peRed: Byte; peGreen: Byte; peBlue: Byte; peFlags: Byte; end; { Logical Palette } PLogPalette = ^TLogPalette; TLogPalette = packed record palVersion: Word; palNumEntries: Word; palPalEntry: array[0..0] of TPaletteEntry; end; PMaxLogPalette = ^TMaxLogPalette; TMaxLogPalette = packed record palVersion: Word; palNumEntries: Word; palPalEntry: array [Byte] of TPaletteEntry; end; |
Frage:
Ich möchte eine JPEG-Datei einem eigenen TBitmap-Objekt zuweisen, damit ich dann im Hintergrund auf das Bild zugreifen und es verändern kann, bzw. Teile davon ausschneiden etc.
Antwort:
Die Unit JPEG in die uses-Liste aufnehmen.
dann ungefähr folgendes:
var xBMPImage : TBitmap; xJPEGImage : TJPEGImage; begin xJPEGImage:=TJPEGImage.Create; try xBMPImage:=TBitmap.Create; try xJPEGImage.LoadFromFile('abcd.jpg'); // -> JPG xBMPImage.Assign(xJPEGImage); // bearbeiten, z.B. abspeichern xBMPImage.SaveToFile('abcd.bmp'); finally xBMPImage.Free; end; finally xJPEGImage.Free; end; end; |
Ein wenig Theorie:
Im Prinzip hält TJPEGImage das JPEG-File im Speicher und gleichzeitig eine nicht bearbeitbare Bitmap, die bei Bedarf erzeugt wird.
TJPEGImage hat nur den Befehl Draw implementiert, d.h. mit Canvas.Draw(X,Y,xJPEGImage); kann man die komplette JPEG auf ein Canvas zeichnen.
Um die Grafik einer JPEG bearbeiten zu können, müssen wir sie einem TBitmap zuordnen:
xBMPImage.Assign(xJPEGImage); |
Jetzt kann man mit dem Bitmap machen, was man möchte.
Bug in der Unit JPEG: (mindestens in Delphi 3)
Ein Bug tritt zu Tage, wenn man eine JPEG-Datei lädt, den Bitmap-Teil aktiviert (z.B. durch Assign oder Anzeigen) und anschließend eine andere JPEG-Datei lädt.
Was passiert:
Der Bitmap-Teil wird nicht für ungültig erklärt, so das nach wie vor das alte Bild im Bitmap-Teil steht.
Workaround:
Vor dem Laden des nächsten Bildes folgende Anweisung benutzen:
with xJPEGImage do Smoothing:=Not Smoothing; |
Das erklärt den Bitmap-Teil für ungültig.
Frage:
Ich habe ein Problem mit TJPEGImage. Ich schreibe gerade eine Dialogbox zur JPEG-Kompression, die eine JPEG-Vorschau hat. Ich möchte nun die komprimierte Version des JPEGs sehen, ich bekomme aber grundsätzlich nur das Original.
Antwort:
Folgende Anweisungen sollten das gewünschte Ergebnis bringen.
var j : TJPEGImage; begin j:=TJPEGImage.Create; try j.Assign(Image1.Picture.Graphic); j.CompressionQuality:=20; j.Compress; j.Smoothing:=Not j.Smoothing; Image2.Picture.Assign(j); finally j.Free; end; end; |
Mail me at webmaster@pjh2.de. | Disclaimer |