// Eine
buchstabenweise erscheinende Schrift wird in ein animiertes
// GIF gespeichert. Wann und wo man das gebrauchen kann, weiß ich
auch
// nicht so ganz genau. Um das Ganze ordnungsgemäß optimieren zu
können
// werden die Buchstaben nicht auf die Form, sondern zunächst auf
eine
// Bitmap geschrieben.
// Es wurden 4
verschiedene Erscheinungsarten der Schrift eingearbeitet:
// Getestet mit RS 10.4 unter Win11
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils,
System.Classes, Vcl.Graphics, Vcl.Imaging.GIFImg,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
Kind = (yNormal, y3D1, y3D2, yOutLine);
var
Txt: String = 'Das ist eine Möglichkeit'#13 + 'Einzelbilder von einer'#13 +
'Buchstabe für Buchstabe'#13 + 'entstehenden Schrift'#13 +
'zum GIF zu verarbeiten.';
PXO, PX, PYO, PY, BAbst, ZAbst, Stelle, TxtH, Tiefe: Integer;
Ordner: String = 'D:\Versuch'; // Zielordner
Fertig: String = 'Animation.gif'; // z.B.
D3, MitOptimierung: Boolean;
Endung: String = '.bmp';
Einzel: String = 'Fase';
FarbeV, FarbeH: TColor;
Zahl: Integer = 0;
bm, hlp: TBitmap;
RCT: TRect;
br: String;
knd: Kind;
procedure Save(cnv: TCanvas);
begin
hlp.SetSize(RCT.Right - RCT.Left, RCT.Bottom - RCT.Top);
hlp.Canvas.CopyRect(hlp.Canvas.Cliprect, cnv, RCT);
hlp.SaveTofile(Ordner + Einzel + Format('%.' + br + 'd', [Zahl]) + Endung);
inc(Zahl);
end;
procedure schrift3d(cnv: TCanvas; Txt: string; x, y: Integer; Art: Boolean);
var
i, offs: Integer;
begin
offs := ord(Art) * Tiefe;
cnv.Font.color := FarbeH;
cnv.Brush.style := bsclear;
i := 1;
repeat
cnv.textout(x + i, y + i, Txt);
inc(i);
until i > Tiefe;
cnv.Font.color := FarbeV;
cnv.textout(x + offs, y + offs, Txt);
end;
procedure OutlineText(cnv: TCanvas; Txt: string; x, y: Integer);
var
i, j: Integer;
begin
with cnv do
begin
Font.color := FarbeV;
Brush.style := bsclear;
for i := -1 to 1 do
for j := -1 to 1 do
textout(x + i, y + j, Txt);
Font.color := FarbeH;
textout(x, y, Txt);
end;
end;
procedure Go(cnv: TCanvas; rectangleonly: Boolean);
var
i, o, w, Rand: Integer;
begin
PX := PXO;
PY := PYO;
Stelle := 0;
Rand := 7; // Seitenränder
TxtH := bm.Canvas.TextHeight(Txt);
for i := 1 to Length(Txt) do
begin
o := ord(Txt[i]);
if o = 13 then
begin
PX := PXO;
inc(PY, TxtH + ZAbst + Tiefe);
end
else
begin
w := bm.Canvas.TextWidth(Txt[i]);
if not rectangleonly then
begin
// Umweg über Bitmap wegen Optimierung
bm.SetSize(w + Tiefe, TxtH + Tiefe);
bm.Canvas.Brush.color := Form1.color;
bm.Canvas.Fillrect(bm.Canvas.Cliprect);
case knd of
y3D1, y3D2:
schrift3d(bm.Canvas, Txt[i], 0, 0, D3);
yOutLine:
OutlineText(bm.Canvas, Txt[i], 0, 0);
else
bm.Canvas.textout(0, 0, Txt[i]);
end;
cnv.Draw(PX, PY, bm);
Application.ProcessMessages;
sleep(50); // für besseres Mitverfolgen
end;
inc(PX, w + BAbst + Tiefe);
if rectangleonly then
begin
if PX > Stelle then
Stelle := PX;
end
else if not(o in [32, 160]) then // für flüssiges Anzeigen
begin
Save(cnv);
end;
end;
end;
RCT := Rect(PXO - Rand, PYO - Rand + Tiefe, Stelle + Rand,
PY + TxtH + Rand + Tiefe);
end;
procedure MakeAnimGIF(Dateiname: TFilename; loop: Byte; optimize: Boolean);
var
GIF: TGIFImage;
Frame: TGIFFrame;
GCExt: TGIFGraphicControlExtension;
LoopExt: TGIFAppExtNSLoop;
i: Integer;
z: Byte;
begin
GIF := TGIFImage.Create;
try
bm.LoadFromFile(Ordner + Einzel + Format('%.' + br + 'd', [0]) + Endung);
Frame := GIF.Add(bm);
if loop <> 1 then
begin
if loop = 0 then // Schleife
z := 0
else
z := pred(loop);
LoopExt := TGIFAppExtNSLoop.Create(Frame);
LoopExt.Loops := z;
end;
GCExt := TGIFGraphicControlExtension.Create(Frame);
GCExt.Delay := 50; // Bild-Dauer Anfangsbild
for i := 1 to pred(Zahl) do
begin
bm.LoadFromFile(Ordner + Einzel + Format('%.' + br + 'd', [i]) + Endung);
Frame := GIF.Add(bm);
GCExt := TGIFGraphicControlExtension.Create(Frame);
if i = pred(Zahl) then
GCExt.Delay := 250 // Bild-Dauer letztes Bild
else
GCExt.Delay := 10;
end;
GIF.OptimizeColorMap;
if optimize then
GIF.optimize([ooCrop, ooMerge, ooColorMap], rmNone, dmNearest, 0);
GIF.SaveTofile(Dateiname);
except
ShowMessage('FEHLER');
end;
FreeAndNil(GIF);
end;
procedure DeleteF(pfad: string);
var
sr: TWin32FindData;
h: THandle;
begin
h := FindFirstFile(PChar(pfad + Einzel + '*' + Endung), sr);
if h <> INVALID_HANDLE_VALUE then
repeat
if sr.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
DeleteFile(pfad + sr.cFileName);
until Findnextfile(h, sr) = False;
Winapi.Windows.FindClose(h);
DeleteFile(pfad + Fertig);
end;
function Vornullen: String; // Vornullenanzahl begrenzen
var
s: string;
begin
s := StringReplace(Txt, #32, '', [rfreplaceall]);
s := StringReplace(s, #160, '', [rfreplaceall]);
Result := IntToStr(Length(IntToStr(Length(s))));
end;
function SetFontAlias(Font: TFont): Boolean;
var
LogFont: TLogFont;
begin
Result := True;
if not MitOptimierung then
exit;
try
if GetObject(Font.Handle, SizeOf(TLogFont), @LogFont) = 0 then
exit;
LogFont.lfQuality := NONANTIALIASED_QUALITY;
Font.Handle := CreateFontIndirect(LogFont);
except
Result := False;
end;
end;
procedure WelcheArt(K: Kind);
begin
knd := K;
case K of
y3D1:
begin
Tiefe := 4; // z.B.
BAbst := -2; // Buchstabenabstand, 0 = normal
D3 := True;
end;
y3D2:
begin
Tiefe := 4;
BAbst := -2;
D3 := False;
end;
yOutLine:
begin
Tiefe := 1;
BAbst := 1;
end;
else
begin
Tiefe := 0;
BAbst := 0;
end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
bm := TBitmap.Create;
hlp := TBitmap.Create;
color := $B0FFFF; // z.B. helles Gelb
if Ansilastchar(Ordner) <> '\' then
Ordner := Ordner + '\';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(hlp);
FreeAndNil(bm);
end;
// --- Beispielaufrufe ---
// Einzelbilder erstellen und speichern
procedure TForm1.Button1Click(Sender: TObject);
begin
Screen.Cursor := crHourglass;
TButton(Sender).Enabled := False;
DeleteF(Ordner); // alte Dateien bei Bedarf löschen
Application.ProcessMessages;
FarbeV := $FF2020; // z.B.
FarbeH := clSilver; // z.B.
MitOptimierung := True; // unbedingt vor "SetFontAlias"
{ MitOptimierung := False; // besseres Schriftbild }
with bm do
begin
Canvas.Brush.color := Form1.color;
Canvas.Font.Name := 'Arial';
Canvas.Font.Size := 18;
Canvas.Font.style := [fsBold];
Canvas.Font.color := FarbeV;
SetFontAlias(Canvas.Font);
end;
WelcheArt(y3D1);
PXO := 50; // links
PYO := 30; // oben
ZAbst := 0; // Zeilenabstand, 0 = normal
Refresh; // falls Wiederholung Form leeren
Go(Canvas, True); // benötigtes Rechteck ermitteln
br := Vornullen;
ForceDirectories(Ordner); // Ordner erzeugen, falls nicht vorhanden
Zahl := 0;
Save(Canvas); // leeres Bild erzeugen
Go(Canvas, False); // Einzel-Bilder speichern
TButton(Sender).Enabled := True;
Screen.Cursor := crDefault;
end;
// gleich anschließend animiertes Gif erstellen
procedure TForm1.Button2Click(Sender: TObject);
var
Loops: Byte;
begin
if Zahl > 1 then
begin
Screen.Cursor := crHourglass;
TButton(Sender).Enabled := False;
Loops := 0;
MakeAnimGIF(Ordner + Fertig, Loops, MitOptimierung);
TButton(Sender).Enabled := True;
Screen.Cursor := crDefault;
end;
end;
end.
|