// Dieser Artikel bezieht sich auf Bitmaps animieren oder ähnliche
// Anwendungen, welche Bitmaps in Strips verarbeiten. Nun stellt
// ein Strip an sich schon eine Optimierung dar. Wenn aber Bilder
// mit einer
Farbtiefe von 24 Bit (oder mehr) benötigt werden, kann
// so ein Strip
(je nach Bildgröße oder Fasenzahl) beim Speichern oder
// beim Einbinden in Ressourcen viel Platz einnehmen.
// Der folgende Code optimiert so einen Strip und erzeugt dafür
// einen neuen Datei-Typ namens *.obs (
Optimierter Bitmap Strip).
// Wird dabei die Variable
Komplett auf False gesetzt, werden zunächst
// die Veränderungen ermittelt, welche von Bild zu Bild auftreten und
// danach noch eine Lauflängenkodierung durchgeführt.
Voraussetzung
// ist jedoch, dass die Grundfläche der Animation immer gleich
// ist (Beispiel 1). Ansonsten fällt die Ermittlung der Veränderungen
// weg
(Komplett = True). Man könnte den Strip auch in ein JPeg wandeln
// und abspeichern. Es kommt dadurch aber meist zu Qualitätsverlusten
// und die Dateien sind oft etwas größer als die *.obs.
// Bei geringen Farbtiefen macht die Optimierung meistens weniger Sinn.
//
(Der Code bezieht sich auf waagerecht verlaufende Strips).


// Getestet mit D4 unter XP

Beispiel 1

Original
Strip.bmp (19 KB)
Komplett = false
Strip.obs (1 KB)
Komplett = true
Strip.obs (3 KB)
Strip.jpg (5 KB)


Beispiel 2

Original
Strip4.bmp (28 KB)
Komplett = false
nicht verwendbar
Komplett = true
Strip4.obs (8 KB)


// Strip Laden, optimieren und als *.obs speichern

type 
  TForm1 = class(TForm) 
    Button1: TButton; 
    procedure FormCreate(Sender: TObject); 
    procedure Button1Click(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
  private 
    { Private-Deklarationen } 
  public 
    procedure vorbereitung(filename: string; bilder: word); 
    procedure veraenderungen(b1, b2: TBitmap); 
    procedure speichern(b: TBitmap); 
    procedure opti(bilder: word; transparent, komplett: boolean); 
    procedure optimiere 
      (filename: string; BildAnzahl: word; transparent, komplett: boolean); 
  end; 
 
var 
  Form1: TForm1; 
 
implementation 
 
{$R *.DFM} 
 
type 
  T24 = packed array[0..2] of Byte; 
 
const 
  flag: byte = $FF; 
  NoCode: byte = 0; 
  MaxCount: byte = $FE; 
 
var 
  basis, hlp, bmp, sv: TBitmap; 
  r, g, b: Byte; 
  breit, fase, links, rechts, oben, unten: word; 
  fs: TFilestream; 
 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
  fs := nil; 
  sv := TBitmap.create; 
  bmp := TBitmap.create; 
  hlp := TBitmap.create; 
  basis := TBitmap.create; 
  basis.pixelformat := pf24bit; 
  hlp.pixelformat := pf24bit; 
  sv.pixelformat := pf24bit; 
end; 
 
procedure TForm1.FormDestroy(Sender: TObject); 
begin 
  bmp.free; 
  basis.free; 
  hlp.free; 
  sv.free; 
  if fs <> nil then 
    fs.free; 
end; 
 
function RLEC(b: TBitmap; dst: TStream): integer; 
var src: TMemorystream; 
  x, y, z: integer; 
  p: PByteArray; 
  function Compr24: Boolean; 
  var 
    zhln: Boolean; 
    Count: byte; 
    T24_2: packed array[0..1] of T24; 
    N24: T24; 
  begin 
    Result := false; 
    if (src.Size - src.Position < SizeOf(T24_2)) then exit; 
    src.ReadBuffer(T24_2, SizeOf(T24_2)); 
    src.Position := src.Position - SizeOf(T24_2); 
    if not comparemem(@T24_2[0], @T24_2[1], 3) then exit; 
    Result := true; 
    src.Position := src.Position + SizeOf(T24_2); 
    Count := High(T24_2) - Low(T24_2) + 1; 
    if (src.Size - src.Position >= SizeOf(N24)) then 
      repeat 
        src.ReadBuffer(N24, SizeOf(N24)); 
        zhln := comparemem(@T24_2[0], @N24, 3); 
        if zhln then Inc(Count) 
        else src.Position := src.Position - SizeOf(T24); 
      until (not zhln) or (Count >= MaxCount) or 
        (src.Size - src.Position < SizeOf(T24)); 
    dst.WriteBuffer(flag, SizeOf(flag)); 
    dst.WriteBuffer(Count, SizeOf(Count)); 
    dst.WriteBuffer(T24_2[0], SizeOf(T24_2[0])); 
  end; 
  procedure nocmpr; 
  var n: byte; 
  begin 
    if (src.Size - src.Position < SizeOf(byte)) then exit; 
    src.ReadBuffer(n, SizeOf(n)); 
    dst.WriteBuffer(n, SizeOf(n)); 
    if (n = flag) then dst.WriteBuffer(NoCode, SizeOf(NoCode)); 
  end; 
begin 
  src := TMemorystream.create; 
  for y := 0 to b.height - 1 do begin 
    p := b.scanline[y]; 
    for x := 0 to b.width - 1 do 
      for z := 0 to 2 do 
        src.writebuffer(p[x * 3 + z], 1); 
  end; 
  src.Position := 0; 
  while (src.Size - src.Position >= SizeOf(byte)) do 
    if not Compr24 then nocmpr; 
  result := dst.size; 
  src.free; 
end; 
 
procedure TForm1.vorbereitung(filename: string; bilder: word); 
var s: string; 
begin 
  if bmp.width * bmp.height * 3 > 1000000 
    then raise exception.create('Speicherbedarf zu groß'); 
  breit := bmp.width div bilder; 
  // bei Bedarf evtl. so etwas: 
  // bmp.TransparentColor := bmp.canvas.pixels[0, 0]; 
  r := GetRValue(bmp.TransparentColor); 
  g := GetGValue(bmp.TransparentColor); 
  b := GetBValue(bmp.TransparentColor); 
  basis.width := breit; 
  hlp.width := breit; 
  basis.height := bmp.height; 
  hlp.height := bmp.height; 
  basis.TransparentColor := bmp.TransparentColor; 
  sv.TransparentColor := bmp.TransparentColor; 
  hlp.TransparentColor := bmp.TransparentColor; 
  sv.transparent := true; 
  s := changefileext(filename, '.obs'); 
  fs := TFilestream.create(s, fmcreate or fmShareExclusive); 
  fase := 0; 
end; 
 
procedure TForm1.speichern(b: TBitmap); 
var w: word; 
  i, p1, p2: integer; 
  dst: TMemorystream; 
begin 
  if (b.width > maxword) or (b.height > maxword) 
    or (b.width < 1) or (b.height < 1) 
    then raise exception.create('Bildmaße fehlerhaft'); 
  dst := TMemorystream.create; 
  w := b.width; 
  fs.writebuffer(w, sizeof(w)); 
  w := b.height; 
  fs.writebuffer(w, sizeof(w)); 
  p1 := fs.position; 
  fs.writebuffer(i, sizeof(i)); 
  i := RLEC(b, dst); 
  dst.position := 0; 
  fs.copyfrom(dst, i); 
  dst.free; 
  p2 := fs.position; 
  fs.position := p1; 
  fs.writebuffer(i, sizeof(i)); 
  fs.position := p2; 
end; 
 
procedure TForm1.veraenderungen(b1, b2: TBitmap); 
var x, y, b3, x3: integer; 
  p1, p2: PBytearray; 
begin 
  b3 := b1.width * 3; 
  links := b1.width; 
  rechts := 0; 
  oben := b1.height; 
  unten := 0; 
  for y := 0 to b1.height - 1 do begin 
    x := 0; 
    p1 := b1.scanline[y]; 
    p2 := b2.scanline[y]; 
    while x < b3 do begin 
      if (p2[x] = p1[x]) and (p2[x + 1] = p1[x + 1]) and (p2[x + 2] = p1[x + 2]) 
        then begin 
        p2[x] := b; 
        p2[x + 1] := g; 
        p2[x + 2] := r; 
      end else begin 
        x3 := x div 3; 
        if x3 < links then links := x3; 
        if x3 > rechts then rechts := x3; 
        if y < oben then oben := y; 
        if y > unten then unten := y; 
      end; 
      inc(x, 3); 
    end; 
  end; 
end; 
 
procedure TForm1.opti(bilder: word; transparent, komplett: boolean); 
var i: integer; 
begin 
  i := bmp.width; 
  fs.writebuffer(i, sizeof(i)); 
  fs.writebuffer(bilder, sizeof(bilder)); 
  fs.writebuffer(transparent, sizeof(transparent)); 
  fs.writebuffer(komplett, sizeof(komplett)); 
  fs.writebuffer(r, 1); 
  fs.writebuffer(g, 1); 
  fs.writebuffer(b, 1); 
  with basis.Canvas do 
    copyrect(cliprect, bmp.canvas, 
      rect(fase * breit, 0, fase * breit + breit, bmp.height)); 
  speichern(basis); 
  while fase < bilder - 1 do begin 
    inc(fase); 
    with hlp.Canvas do 
      copyrect(cliprect, bmp.canvas, 
        rect(fase * breit, 0, fase * breit + breit, bmp.height)); 
    if komplett then begin 
      links := 0; 
      oben := 0; 
      sv.assign(hlp); 
    end else begin 
      veraenderungen(basis, hlp); 
      sv.width := rechts - links - 1; 
      sv.height := unten - oben; 
      sv.canvas.copyrect(sv.canvas.cliprect, hlp.canvas, 
        rect(links, oben, rechts, unten)); 
    end; 
    fs.writebuffer(links, sizeof(links)); 
    fs.writebuffer(oben, sizeof(oben)); 
    speichern(sv); 
    basis.canvas.draw(links, oben, sv); 
  end; 
end; 
 
procedure TForm1.optimiere 
  (filename: string; Bildanzahl: word; transparent, komplett: boolean); 
begin 
  bmp.loadfromfile(filename); 
  vorbereitung(filename, Bildanzahl); 
  opti(BildAnzahl, transparent, komplett); 
  fs.free; 
  fs := nil; 
end; 
 
// Beispiel 1
 
procedure TForm1.Button1Click(Sender: TObject); 
var filename: string; 
  Bildzahl: word; 
  transparent, komplett: boolean; 
begin 
  filename := 'c:\strip.bmp'; 
  Bildzahl := 6; 
  transparent := true; 
  komplett := false; 
  optimiere(filename, Bildzahl, transparent, komplett); 
end; 


//------------------------------------------------------------------------
// *.obs laden, decodieren und Strip erstellen 
type 
  TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
  private 
    { Private-Deklarationen } 
  public 
    procedure aufteilen; 
    procedure zuweisen(s: TStream; b: TBitmap); 
    procedure build(filename: string); 
  end; 
 
var 
  Form1: TForm1; 
 
implementation 
 
{$R *.DFM} 
 
type 
  T24 = packed array[0..2] of Byte; 
  rc = record 
    bm: TBitmap; 
    w, h, l, o: word; 
  end; 
 
const 
  flag: byte = $FF; 
 
var 
  fs: TFilestream; 
  bild: array of rc; 
  transparent, komplett: boolean; 
  r, g, b: byte; 
  bmp: TBitmap; 
  breit: integer; 
 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
  bmp := TBitmap.create; 
  fs := nil; 
end; 
 
procedure TForm1.FormDestroy(Sender: TObject); 
var x: integer; 
begin 
  if fs <> nil then 
    fs.free; 
  for x := 0 to high(bild) do 
    bild[x].bm.free; 
  bild := nil; 
  bmp.free; 
end; 
 
procedure RLED(src, dst: TStream); 
var 
  ccount, n: byte; 
  procedure DC24(Count: byte); 
  var 
    i: byte; 
    Mn: T24; 
  begin 
    src.ReadBuffer(Mn, SizeOf(Mn)); 
    for i := Count downto 1 do 
      dst.WriteBuffer(Mn, SizeOf(Mn)); 
  end; 
begin 
  src.Position := 0; 
  while (src.Size - src.Position >= SizeOf(byte)) do 
  begin 
    src.ReadBuffer(n, SizeOf(n)); 
    if (n = flag) then 
    begin 
      src.ReadBuffer(ccount, SizeOf(ccount)); 
      if (ccount = 0) then dst.WriteBuffer(n, SizeOf(n)) 
      else DC24(ccount); 
    end 
    else dst.WriteBuffer(n, SizeOf(n)); 
  end; 
end; 
 
procedure TForm1.zuweisen(s: TStream; b: TBitmap); 
var x, y, z: integer; 
  p: PByteArray; 
begin 
  for y := 0 to b.height - 1 do begin 
    p := b.scanline[y]; 
    for x := 0 to b.width - 1 do 
      for z := 0 to 2 do 
        s.readbuffer(p[x * 3 + z], 1); 
  end; 
end; 
 
procedure TForm1.aufteilen; 
var w: word; 
  x, i: integer; 
  src, dst: TMemoryStream; 
begin 
  src := TMemoryStream.create; 
  dst := TMemoryStream.create; 
  fs.readbuffer(breit, sizeof(breit)); 
  fs.readbuffer(w, sizeof(w)); 
  setlength(bild, w); 
  for x := 0 to high(bild) do begin 
    bild[x].bm := TBitmap.create; 
    bild[x].bm.pixelformat := pf24bit; 
  end; 
  bild[0].o := 0; 
  bild[0].l := 0; 
  fs.readbuffer(transparent, sizeof(transparent)); 
  fs.readbuffer(komplett, sizeof(komplett)); 
  fs.readbuffer(r, 1); 
  fs.readbuffer(g, 1); 
  fs.readbuffer(b, 1); 
  fs.readbuffer(bild[0].w, sizeof(w)); 
  bild[0].bm.width := bild[0].w; 
  fs.readbuffer(bild[0].h, sizeof(w)); 
  bild[0].bm.height := bild[0].h; 
  fs.readbuffer(x, sizeof(x)); 
  src.copyfrom(fs, x); 
  RLED(src, dst); 
  dst.position := 0; 
  zuweisen(dst, bild[0].bm); 
  for x := 1 to high(bild) do begin 
    src.size := 0; 
    dst.size := 0; 
    fs.readbuffer(bild[x].l, sizeof(w)); 
    fs.readbuffer(bild[x].o, sizeof(w)); 
    fs.readbuffer(bild[x].w, sizeof(w)); 
    fs.readbuffer(bild[x].h, sizeof(w)); 
    fs.readbuffer(i, sizeof(i)); 
    src.copyfrom(fs, i); 
    RLED(src, dst); 
    dst.position := 0; 
    bild[x].bm.width := bild[x].w; 
    bild[x].bm.height := bild[x].h; 
    zuweisen(dst, bild[x].bm); 
    bild[x].bm.transparentcolor := RGB(r, g, b); 
    bild[x].bm.transparent := not komplett; 
  end; 
  src.free; 
  dst.free; 
end; 
 
procedure TForm1.build(filename: string); 
var x, y: integer; 
begin 
  fs := TFilestream.create(filename, fmOpenRead or fmShareExclusive); 
  aufteilen; 
  fs.free; 
  fs := nil; 
  bmp.height := bild[0].h; 
  bmp.width := bild[0].w * length(bild); 
  if bmp.width <> breit 
    then raise exception.create('Abmaße oder Bilderzahl fehlerhaft'); 
  for x := 0 to high(bild) do 
    bmp.canvas.draw(x * bild[0].w, 0, bild[0].bm); 
  for x := high(bild) downto 1 do 
    for y := 1 to x do 
      bmp.canvas.draw(x * bild[0].w + bild[y].l, bild[y].o, bild[y].bm); 
  bmp.transparentcolor := RGB(r, g, b); 
  bmp.transparent := transparent; 
end; 
 
 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  build('c:\Strip.obs'); 
  // und zur Kontrolle: 
  Canvas.draw(10, 10, bmp); 
end; 


 

Zugriffe seit 6.9.2001 auf Delphi-Ecke