// Konfetti-Schrift in drei Varianten. Bei den ersten beiden werden fertige
// Worte umgesetzt, währen bei der dritten die Buchstaben einzeln eingetippt
// werden können.


// Getestet mit RS 10.4 unter Win11

 

// Variante 1 (Outline)
// Aus vier kleinen Elementen (siehe Tabelle) wird die Schrift erzeugt.
// Voraussetzung ist, dass die Schriftgröße entsprechend hoch eingestellt
// wurde.

Herz.bmp Kreuz.bmp Raute.bmp Rund.bmp

 

const 
  max = 5; 
 
type 
  aoc = array [0 .. max] of TColor; 
 
var 
  Farbe: aoc = ($4040FF, clYellow, clLime, clAqua, clFuchsia, $FF9090); 
  Herz, Kreuz, Raute, Rund: array [0 .. max] of TBitmap; 
  Zeichen: array of TBitmap; 
  Mass, M2, MerkC, MerkF: Integer; 
  Basis: TBitmap; 
  r, g, b: Byte; 
 
procedure Erstellen(bmp: TBitmap); 
var 
  X, Y, B3, RA, RB: Integer; 
  p: PByteArray; 
  Bild: TBitmap; 
begin 
  bmp.PixelFormat := pf24Bit; 
  Y := 0; 
  B3 := bmp.Width * 3; 
  while Y < bmp.Height do 
  begin 
    p := bmp.ScanLine[Y]; 
    X := 0; 
    while X < B3 do 
    begin 
      if (b = p[X]) and (g = p[X + 1]) and (r = p[X + 2]) then 
      begin 
        repeat 
          RA := Random(max + 1); 
        until (RA <> MerkC) and (GetPixel(bmp.Canvas.Handle, X div 3, Y - M2) <> 
          GetPixel(Herz[RA].Canvas.Handle, M2, M2)); 
        MerkC := RA; 
        repeat 
          RB := Random(4); 
        until RB <> MerkF; 
        MerkF := RB; 
        case RB of 
          0: 
            Bild := Herz[RA]; 
          1: 
            Bild := Kreuz[RA]; 
          2: 
            Bild := Rund[RA]; 
        else 
          Bild := Raute[RA]; 
        end; 
        bmp.Canvas.Draw(X div 3 - M2, Y - M2, Bild); 
      end; 
      inc(X, 3); 
    end; 
    inc(Y); 
  end; 
end; 
 
function OutlineText(bm: TBitmap; Schrft, Txt: string; SG, Abstand: Integer; 
  Rand, Buchst, Flaeche: TColor; Transparent: Boolean): Boolean; 
var 
  I, J, X, W: Integer; 
  SZ: TSize; 
  LogFont: TLogFont; 
begin 
  Result := False; 
  Rand := ColorToRGB(Rand); 
  Flaeche := ColorToRGB(Flaeche); 
  Buchst := ColorToRGB(Buchst); 
  if (Rand = clBlack) or (Rand = Flaeche) or (Rand = Buchst) then 
    exit; 
  for I := 0 to max do 
    if Rand = ColorToRGB(Farbe[I]) then 
      exit; 
  M2 := Mass div 2; 
  W := 0; 
  MerkC := -1; 
  MerkF := -1; 
  r := GetRValue(Rand); 
  g := GetGValue(Rand); 
  b := GetBValue(Rand); 
  SetLength(Zeichen, Length(Txt)); 
  for X := 0 to High(Zeichen) do 
  begin 
    Zeichen[X] := TBitmap.Create; 
    with Zeichen[X], Canvas do 
    begin 
      Font.Name := Schrft; 
      Font.Size := SG; 
      Brush.Color := Flaeche; 
      GetObject(Font.Handle, SizeOf(TLogFont), @LogFont); 
      LogFont.lfQuality := NONANTIALIASED_QUALITY; 
      Font.Handle := CreateFontIndirect(LogFont); 
      SZ := TextExtent(Txt[X + 1]); 
      SetSize(SZ.cx + Mass, SZ.cy + Mass); 
      inc(W, SZ.cx + Mass + Abstand); 
      Font.Color := Rand; 
      Brush.style := bsclear; 
      for I := 0 to 2 do 
        for J := 0 to 2 do 
          TextOut(M2 + I, M2 + J, Txt[X + 1]); 
      Font.Color := Buchst; 
      TextOut(M2 + 1, M2 + 1, Txt[X + 1]); 
    end; 
  end; 
  with bm, Canvas do 
  begin 
    Brush.Color := Flaeche; 
    I := 0; 
    Width := W; 
    Height := SZ.cy + Mass; 
    Fillrect(Cliprect); 
    for X := 0 to High(Zeichen) do 
    begin 
      Erstellen(Zeichen[X]); 
      Draw(I, 0, Zeichen[X]); 
      inc(I, Zeichen[X].Width + Abstand); 
      FreeAndNil(Zeichen[X]); 
    end; 
  end; 
  Zeichen := nil; 
  bm.Transparent := Transparent; 
  Result := True; 
end; 
 
procedure TForm1.FormCreate(Sender: TObject); 
var 
  I: Integer; 
begin 
  for I := 0 to max do 
  begin 
    Herz[I] := TBitmap.Create; 
    Herz[I].LoadFromFile('Herz.bmp'); 
    Herz[I].Canvas.Brush.Color := Farbe[I]; 
    Herz[I].Canvas.FloodFill(Herz[I].Width div 2, Herz[I].Height div 2, clWhite, 
      fsSurface); 
    Herz[I].Transparent := True; 
    Kreuz[I] := TBitmap.Create; 
    Kreuz[I].LoadFromFile('Kreuz.bmp'); 
    Kreuz[I].Canvas.Brush.Color := Farbe[I]; 
    Kreuz[I].Canvas.FloodFill(Kreuz[I].Width div 2, Kreuz[I].Height div 2, 
      clWhite, fsSurface); 
    Kreuz[I].Transparent := True; 
    Raute[I] := TBitmap.Create; 
    Raute[I].LoadFromFile('Raute.bmp'); 
    Raute[I].Canvas.Brush.Color := Farbe[I]; 
    Raute[I].Canvas.FloodFill(Raute[I].Width div 2, Raute[I].Height div 2, 
      clWhite, fsSurface); 
    Raute[I].Transparent := True; 
    Rund[I] := TBitmap.Create; 
    Rund[I].LoadFromFile('Rund.bmp'); 
    Rund[I].Canvas.Brush.Color := Farbe[I]; 
    Rund[I].Canvas.FloodFill(Rund[I].Width div 2, Rund[I].Height div 2, clWhite, 
      fsSurface); 
    Rund[I].Transparent := True; 
  end; 
  Basis := TBitmap.Create; 
  Mass := Herz[0].Width; 
end; 
 
procedure TForm1.FormDestroy(Sender: TObject); 
var 
  I: Integer; 
begin 
  FreeAndNil(Basis); 
  for I := 0 to max do 
  begin 
    FreeAndNil(Herz[I]); 
    FreeAndNil(Kreuz[I]); 
    FreeAndNil(Raute[I]); 
    FreeAndNil(Rund[I]); 
  end; 
end; 
 
// Beispiel 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  if OutlineText(Basis, 'Arial', 'DELPHI', 110, 0, clGray, Color, clSilver, True) 
  then 
    Canvas.Draw(10, 80, Basis) 
  else 
    ShowMessage('Farben überprüfen!'); 
end;
 
// ---------------------------------------------------------------
// Variante 2 Leicht vereinfachte Variante (ohne Outline) für ein wenig
// kleinere Schriften. Die Variablen sind dieselben wie bei Variante 1

procedure Erstellen(bmp: TBitmap); 
var 
  X, Y, B3, RA, RB: Integer; 
  p: PByteArray; 
  Bild: TBitmap; 
begin 
  bmp.PixelFormat := pf24Bit; 
  Y := 0; 
  B3 := bmp.Width * 3; 
  while Y < bmp.Height do 
  begin 
    p := bmp.ScanLine[Y]; 
    X := 0; 
    while X < B3 do 
    begin 
      if (b = p[X]) and (g = p[X + 1]) and (r = p[X + 2]) then 
      begin 
        repeat 
          RA := Random(max + 1); 
        until (RA <> MerkC) and (GetPixel(bmp.Canvas.Handle, X div 3, Y - M2) <> 
          GetPixel(Herz[RA].Canvas.Handle, M2, M2)); 
        MerkC := RA; 
        repeat 
          RB := Random(4); 
        until RB <> MerkF; 
        MerkF := RB; 
        case RB of 
          0: 
            Bild := Herz[RA]; 
          1: 
            Bild := Kreuz[RA]; 
          2: 
            Bild := Rund[RA]; 
        else 
          Bild := Raute[RA]; 
        end; 
        bmp.Canvas.Draw(X div 3 - M2, Y - M2, Bild); 
      end; 
      inc(X, 9); 
    end; 
    inc(Y, 3); 
  end; 
end;
function MakeText(bm: TBitmap; Schrft, Txt: string; SG: Integer; 
  Buchst, Flaeche: TColor; Transparent: Boolean): Boolean; 
var 
  I, X, W: Integer; 
  SZ: TSize; 
  LogFont: TLogFont; 
begin 
  Result := False; 
  Flaeche := ColorToRGB(Flaeche); 
  Buchst := ColorToRGB(Buchst); 
  if (Buchst = clBlack) or (Buchst = Flaeche) then 
    exit; 
  for I := 0 to max do 
    if Buchst = ColorToRGB(Farbe[I]) then 
      exit; 
  M2 := Mass div 2; 
  W := 0; 
  MerkC := -1; 
  MerkF := -1; 
  r := GetRValue(Buchst); 
  g := GetGValue(Buchst); 
  b := GetBValue(Buchst); 
  SetLength(Zeichen, Length(Txt)); 
  for X := 0 to High(Zeichen) do 
  begin 
    Zeichen[X] := TBitmap.Create; 
    with Zeichen[X], Canvas do 
    begin 
      Font.Name := Schrft; 
      Font.Size := SG; 
      Brush.Color := Flaeche; 
      GetObject(Font.Handle, SizeOf(TLogFont), @LogFont); 
      LogFont.lfQuality := NONANTIALIASED_QUALITY; 
      Font.Handle := CreateFontIndirect(LogFont); 
      SZ := TextExtent(Txt[X + 1]); 
      SetSize(SZ.cx + Mass, SZ.cy + Mass); 
      inc(W, SZ.cx + Mass); 
      Brush.style := bsclear; 
      Font.Color := Buchst; 
      TextOut(M2 + 1, M2 + 1, Txt[X + 1]); 
    end; 
  end; 
  with bm, Canvas do 
  begin 
    Brush.Color := Flaeche; 
    I := 0; 
    Width := W; 
    Height := SZ.cy + Mass; 
    Fillrect(Cliprect); 
    for X := 0 to High(Zeichen) do 
    begin 
      Erstellen(Zeichen[X]); 
      Draw(I, 0, Zeichen[X]); 
      inc(I, Zeichen[X].Width); 
      FreeAndNil(Zeichen[X]); 
    end; 
  end; 
  Zeichen := nil; 
  bm.Transparent := Transparent; 
  Result := True; 
end; 
 
procedure TForm1.FormCreate(Sender: TObject); 
var 
  I: Integer; 
begin 
  for I := 0 to max do 
  begin 
    Herz[I] := TBitmap.Create; 
    Herz[I].LoadFromFile('Herz.bmp'); 
    Herz[I].Canvas.Brush.Color := Farbe[I]; 
    Herz[I].Canvas.FloodFill(Herz[I].Width div 2, Herz[I].Height div 2, clWhite, 
      fsSurface); 
    Herz[I].Transparent := True; 
    Kreuz[I] := TBitmap.Create; 
    Kreuz[I].LoadFromFile('Kreuz.bmp'); 
    Kreuz[I].Canvas.Brush.Color := Farbe[I]; 
    Kreuz[I].Canvas.FloodFill(Kreuz[I].Width div 2, Kreuz[I].Height div 2, 
      clWhite, fsSurface); 
    Kreuz[I].Transparent := True; 
    Raute[I] := TBitmap.Create; 
    Raute[I].LoadFromFile('Raute.bmp'); 
    Raute[I].Canvas.Brush.Color := Farbe[I]; 
    Raute[I].Canvas.FloodFill(Raute[I].Width div 2, Raute[I].Height div 2, 
      clWhite, fsSurface); 
    Raute[I].Transparent := True; 
    Rund[I] := TBitmap.Create; 
    Rund[I].LoadFromFile('Rund.bmp'); 
    Rund[I].Canvas.Brush.Color := Farbe[I]; 
    Rund[I].Canvas.FloodFill(Rund[I].Width div 2, Rund[I].Height div 2, clWhite, 
      fsSurface); 
    Rund[I].Transparent := True; 
  end; 
  Basis := TBitmap.Create; 
  Mass := Herz[0].Width; 
end; 
 
procedure TForm1.FormDestroy(Sender: TObject); 
var 
  I: Integer; 
begin 
  FreeAndNil(Basis); 
  for I := 0 to max do 
  begin 
    FreeAndNil(Herz[I]); 
    FreeAndNil(Kreuz[I]); 
    FreeAndNil(Raute[I]); 
    FreeAndNil(Rund[I]); 
  end; 
end; 
 
// Beispiel 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
  if MakeText(Basis, 'Calibri', 'Delphi', 65, clGray, $D6FCE7, False) then 
    Canvas.Draw(10, 80, Basis) 
  else 
    ShowMessage('Farben überprüfen!'); 
end; 
 
// ---------------------------------------------------------------
// Variante 3
// Die Buchstaben können hierbei eingetippt werden. Allerdings gibt es
// der Einfachheit halber lediglich eine sehr simple Methode zum Löschen
// einzelner Zeichen. Mann kann nur innerhalb einer Zeile vom Zeilenende
// her mit der Rücktaste eine Entfernung vornehmen. Das komplette Löschen
// einer Zeile ist auch nicht möglich. Es bleibt also genügend Luft, um
// den Code zu erweitern. Mit der ENTER-Taste kann (nur am Ende) eine neue
// Zeile erzeugt werden.


 
const 
  sm = 6; 
  max = 8; 
  mass = 7; 
  m2 = mass div 2; 
 
type 
  aoc = array [0 .. max] of TColor; 
 
var 
  Farbe: aoc = (clYellow, clLime, clAqua, clFuchsia, clRed, $AAFF, $5050FF, 
    $FF9090, $60B0FF); 
  XX, YY, MerkX, MerkY, Breit, Hoch, Zeile: Integer; 
  Rund: array [0 .. max] of TBitmap; 
  Flaeche, Buchst, Blink: TColor; 
  Zeichen, H1, H2: TBitmap; 
  Schreibmarke: TImage; 
  lg: array of Integer; 
  r, g, b: Byte; 
  Size: TSize; 
 
function TForm1.Vorbereitung: Boolean; 
var 
  I: Integer; 
  LogFont: TLogFont; 
begin 
  Result := False; 
  Buchst := 1; 
  Flaeche := ColorToRGB(Flaeche); 
  if Flaeche = clBlack then 
    Flaeche := 1; 
  if Buchst = Flaeche then 
    inc(Buchst); 
  for I := 0 to max do 
    if Buchst = ColorToRGB(Farbe[I]) then 
    begin 
      ShowMessage('Farben unzulässig'); 
      exit; 
    end; 
  XX := 0; 
  YY := 0; 
  MerkX := XX; 
  MerkY := YY; 
  Zeile := 0; 
  SetLength(lg, 1); 
  lg[0] := 0; 
  Image1.Canvas.Brush.Color := clBtnFace; // z.B. 
  Image1.Canvas.FillRect(Image1.Canvas.ClipRect); 
  r := GetRValue(Buchst); 
  g := GetGValue(Buchst); 
  b := GetBValue(Buchst); 
  with Zeichen, Canvas do 
  begin 
    Font.Name := 'Courier New'; // unbedingt Schriftart mit fester Breite 
    Font.Size := 37; // empfohlen 
    Font.Style := []; 
    Brush.Color := Flaeche; 
    Font.Color := Buchst; 
    GetObject(Font.Handle, SizeOf(TLogFont), @LogFont); 
    LogFont.lfQuality := NONANTIALIASED_QUALITY; 
    Font.Handle := CreateFontIndirect(LogFont); 
    Size := TextExtent('W'); 
    Breit := Size.cx + mass; 
    Hoch := Size.cy + m2; 
    SetSize(Breit, Hoch); 
  end; 
  with Schreibmarke do 
  begin 
    Autosize := True; 
    with Picture.Bitmap, Canvas do 
    begin 
      Brush.Color := Blink; 
      Width := sm; 
      Height := Hoch; 
    end; 
  end; 
  H1.Canvas.Brush.Color := Flaeche; 
  H1.SetSize(sm, Hoch); 
  H2.Canvas.Brush.Color := Image1.Canvas.Brush.Color; 
  H2.SetSize(sm, Hoch); 
  Stelle; 
  Result := True; 
end; 
 
procedure TForm1.Erstellen(bmp: TBitmap); 
var 
  I, J, B3: Integer; 
  p: PByteArray; 
begin 
  J := 0; 
  B3 := bmp.Width * 3; 
  while J < bmp.Height do 
  begin 
    p := bmp.ScanLine[J]; 
    I := 0; 
    while I < B3 do 
    begin 
      if (b = p[I]) and (g = p[I + 1]) and (r = p[I + 2]) then 
      begin 
        TransparentBlt(bmp.Canvas.Handle, I div 3 - m2, J - m2, mass, mass, 
          Rund[Random(max + 1)].Canvas.Handle, 0, 0, mass, mass, clWhite); 
      end; 
      inc(I, 6); 
    end; 
    inc(J, 2); 
  end; 
end; 
 
procedure TForm1.Buchstabe(C: Char); 
var 
  b: Boolean; 
  procedure go; 
  begin 
    Image1.Canvas.Draw(XX, YY, Zeichen); 
    Image1.Canvas.Draw(XX + Breit, YY, H2); 
  end; 
 
begin 
  b := C = #8; 
  if C = #13 then 
  begin 
    inc(YY, Hoch); 
    inc(Zeile); 
    if Zeile = Length(lg) then 
    begin 
      SetLength(lg, succ(Length(lg))); 
      lg[Zeile] := 0; 
      XX := MerkX; 
    end; 
    ZumEnde; 
  end 
  else 
    with Zeichen, Canvas do 
    begin 
      if b then 
        Brush.Color := Image1.Canvas.Brush.Color 
      else 
        Brush.Color := Flaeche; 
      FillRect(ClipRect); 
      if b then 
      begin 
        if XX = MerkX then 
        begin 
          if YY = MerkY then 
            exit; 
          dec(YY, Hoch); 
          dec(Zeile); 
          XX := MerkX + lg[Zeile] * Breit; 
        end 
        else 
        begin 
          dec(XX, Breit); 
          dec(lg[Zeile]); 
          go; 
        end; 
      end 
      else 
      begin 
        TextOut(m2, m2, C); 
        Erstellen(Zeichen); 
        go; 
        inc(lg[Zeile]); 
        inc(XX, Breit); 
      end; 
    end; 
  Stelle; 
end; 
 
procedure TForm1.FormCreate(Sender: TObject); 
var 
  I: Integer; 
begin 
  Self.KeyPreview := True; 
  H1 := TBitmap.Create; 
  H2 := TBitmap.Create; 
  Zeichen := TBitmap.Create; 
  Zeichen.PixelFormat := pf24Bit; 
  for I := 0 to max do 
  begin 
    Rund[I] := TBitmap.Create; 
    Rund[I].SetSize(mass, mass); 
    Rund[I].Canvas.Ellipse(0, 0, mass, mass); 
    Rund[I].Canvas.Brush.Color := Farbe[I]; 
    Rund[I].Canvas.FloodFill(m2, m2, clWhite, fsSurface); 
  end; 
  Timer1.Enabled := False; 
  Timer1.Interval := 250; 
  Schreibmarke := TImage.Create(Self); 
  Schreibmarke.Visible := False; 
  Schreibmarke.parent := Form1; 
  // z.B. ------------------------- 
  Image1.Width := 10000; 
  Image1.Height := Image1.Width; 
  Image1.Left := 10; 
  Image1.Top := 10; 
  // ------------------------------- 
end; 
 
procedure TForm1.FormDestroy(Sender: TObject); 
var 
  I: Integer; 
begin 
  for I := 0 to max do 
    FreeAndNil(Rund[I]); 
  FreeAndNil(H1); 
  FreeAndNil(H2); 
  FreeAndNil(Zeichen); 
  FreeAndNil(Schreibmarke); 
  lg := nil; 
end; 
 
procedure TForm1.FormShow(Sender: TObject); 
begin 
  // muss alles nicht an dieser Stelle stehen 
  Flaeche := $DFFFFF; 
  Blink := clBlack; 
  Timer1.Enabled := Vorbereitung; 
  // --------------------------------- 
end; 
 
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; 
  Shift: TShiftState; X, Y: Integer); 
begin 
  Y := Y div Hoch; 
  if Y > High(lg) then 
    exit 
  else 
  begin 
    Zeile := Y; 
    ZumEnde; 
  end; 
end; 
 
procedure TForm1.Stelle; 
begin 
  Schreibmarke.Left := XX + Image1.Left; 
  Schreibmarke.Top := YY + Image1.Top; 
  // leere Zeile kennzeichnen: 
  // (wirkt nur bei unterschiedlicher Farbe zum Hintergrund) 
  Image1.Canvas.Draw(XX, YY, H1); 
  // --------------------------------------- 
end; 
 
procedure TForm1.Timer1Timer(Sender: TObject); 
begin 
  Schreibmarke.Visible := not Schreibmarke.Visible; 
end; 
 
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; 
  Shift: TShiftState); 
begin 
  if Key = VK_DOWN then 
  begin 
    if (Zeile >= High(lg)) then 
      exit; 
    inc(Zeile); 
    ZumEnde; 
  end 
  else if Key = VK_UP then 
  begin 
    if Zeile > 0 then 
    begin 
      dec(Zeile); 
      ZumEnde; 
    end; 
  end; 
end; 
 
procedure TForm1.ZumEnde; 
begin 
  if XX <> lg[Zeile] * Breit then 
    XX := lg[Zeile] * Breit; 
  YY := Zeile * Hoch; 
  Stelle; 
end; 
 
procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); 
begin 
  if Timer1.Enabled then 
    Buchstabe(Key); 
end;

Zugriffe seit 6.9.2001 auf Delphi-Ecke