Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi canvas.rectangle transparent? (https://www.delphipraxis.net/189049-canvas-rectangle-transparent.html)

Mattze 28. Apr 2016 13:59

canvas.rectangle transparent?
 
Hallo,

ich weiß, das Thema gibt es oft, aber irgendwie finde ich nichts passendes und mit einer transparenten Bitmap scheint es mir sehr, "mit Kanonen auf Spatzen schießen" zu sein.

Mein Problem:
Auf einem Canvas wurde ein Text ausgegeben. Ich kenne diesen Text nicht.
Ich weiß lediglich das Rechteck, in dem er steht. (Allerdings weiß ich auch nicht, wie er da drin steht.)
Dieses Rechteck würde ich gerne mit einer anderen Farbe füllen, so, dass der Text lesbar bleibt.
Sozusagen würde ich also den Text gerne mit einem eingefärbten Hintergrund hervorheben.

Wie kann man das am besten schnell und effizient machen?

Gruß
Mattze

Blup 28. Apr 2016 16:46

AW: canvas.rectangle transparent?
 
Da die Pixel des Textes an den Ecken und Schrägen z.T. halbtransparent gezeichnet sind, scheint dort der Hintergrund durch.
Das erschwert die Sache etwas. Ist der Text schwarz gezeichnet, kann man diesen Umstand nutzen.

In diesem Beispiel wird nur die Helligkeit des Ausgangsbildes übernommen.
Die graue Bereiche werden eingefärbt.
Weiße oder schwarze Flächen bleiben unverändert.
Delphi-Quellcode:
// Pixelformat  pf24Bit
// NewHue       0..100
// NewSaturation 0..100

var
  NewHue, NewSaturation, Hue, Luminance, Saturation: Word;
  p: ^TBGR;
  c: TColor;
  x, y: Integer;
begin

{...}
    for y := 0 to Height - 1 do
    begin
      p := ScanLine[y];
      for x := 0 to Width - 1 do
      begin
        c := RGB(p^.R, p^.G, p^.B);
        ColorRGBToHLS(c, Hue, Luminance, Saturation);
        c := ColorHLSToRGB(NewHue, Luminance, NewSaturation);
        p^.B := GetBValue(c);
        p^.G := GetGValue(c);
        p^.R := GetRValue(c);
        Inc(p);
      end;
    end;
{...}
Alternativ könnte man z.B. die Helligkeit anheben (z.B. Luminance := Round(Luminance * 0.2) ).

Mattze 28. Apr 2016 18:22

AW: canvas.rectangle transparent?
 
Vielen Dank für die Antwort.

Ich glaube, ich muss betonen, dass ich mit Delphi 7 pro arbeite.

Also: Was ist TBGR und woher kriege ich ein Scanline ohne bitmap?
(TBGR gibt's in meiner Hilfe nicht und Scanline kennt er nur als Methode einer Bitmap.)

Das Problem dürfte sein, wie ich da eine neue Farbe reinkriege.

Gruß
Mattze

Aviator 28. Apr 2016 19:18

AW: canvas.rectangle transparent?
 
Zitat:

Zitat von Mattze (Beitrag 1336986)
(TBGR gibt's in meiner Hilfe nicht und Scanline kennt er nur als Methode einer Bitmap.)

Ohne das ich das jetzt kenne, aber schonmal mit TRGB probiert? Farben werden normalerweise mit RGB angegeben. Kann natürlich sein, dass ich mich jetzt im Bezug auf diese Funktion vertue. :roll:

Mattze 29. Apr 2016 05:38

AW: canvas.rectangle transparent?
 
Das ist mir natürlich auch aufgefallen und selbstverständlich habe ich das auch probiert.
Ich hab's nur nicht explizit geschrieben, weil es ein BGR wohl manchmal auch gibt.

Es sei denn, dort, wo ich es gelesen habe, hat man sich verschrieben.

Wobei das Scanline-Problem so oder so bleibt!

Interessant ist, dass zu diesem Problem kaum jemand etwas sagen kann (oder will).
Schade!

Gruß
Mattze

Medium 29. Apr 2016 07:57

AW: canvas.rectangle transparent?
 
Ich habe mich anfangs zurück gehalten, weil ich glaubte das Problem nicht richtig verstanden zu haben. Aber okay: Für mich hört sich das so an, als willst du doch bloß einfach ein Rechteck auf deinen Canvas malen oder? Also einfach Canvas.Pen und .Brush auf das gewünschte einstellen, und mit Canvas.Rectangle() den Bereich übermalen. Wo da jetzt genau Transparenz mit rein spielt ist mir nicht wirklich klar. Bzw. bin ich nicht sicher wirklich das Problem erkannt zu haben, weil die genannte Lösung hört sich für mich zu einfach an, als dass du da nicht drauf gekommen wärst :)

Mattze 29. Apr 2016 08:43

AW: canvas.rectangle transparent?
 
Richtig, ganz so einfach ist es eben nicht.

Ganz allgemein, vielleicht verständlicher, auf eine ganzen Canvas bezogen:
Auf dem Canvas steht ein Text (oder sonsteirgendwas), den ich nicht kenne und auch nicht habe.
Ich kenne nur die Koordinaten eines Rechtecks, in dem das irgendwie drin steht.
(Rechtsbündig, linksbündig, mittig...)
Ich weiß jedoch, das der Text extrem wichtig ist.
Also möchte ich das Rechteck, in dem der Text steht, farblich hervorheben -sprich:
die "Hintergrundfarbe" in diesem Rechteck ändern.
"Hintergrundfarbe" deshalb, weil der Text natürlich lesbar bleiben soll.
Z. Bsp. steht da auf weißem Grund "Alarm!". (Den genauen Text kenne ich aber nicht, nur das es etwas mit "Alarm!" zu tun hat.)
Ich hätte da aber gerne dann eine hellroten Hintergrund.
Dann steht da "Alarm!" auf hellrotem Hintergrund.

Es geht also nicht ums ÜBERmalen!

Alles klaro?

Gruß
Mattze

Neutral General 29. Apr 2016 09:17

AW: canvas.rectangle transparent?
 
Prinzipiell kannst du mit Canvas.Rectangle und BrushStyle = bsClear ein Rechteck um den Text ziehen und dann per FloodFill den Hintergrund des Rechtecks einfärben ohne den im Rechteck enthaltenen Text zu übermalen. ABER wie Blup in seinem Post schon gesagt hat wird es aufgrund der halbtransparenten Pixel (bzw. Grauabstufungen am Rand der Buchstaben) wahrscheinlich etwas pixelig aussehen.

Mit Blups Code wirst du evtl ein besseres Ergebnis erzielen. Sein Algorithmus geht theoretisch auch mit Canvas.Pixels statt mit ScanLine, aber das ist sehr langsam.
Du könntest dir alternativ ein temporäres Bitmap in der Größe des Bereichs erstellen und mit Canvas.CopyRect oder BitBlt den Ursprungsbereich in ein Bitmap kopieren, dann mit Blups Algorithmus deine Änderung vornehmen und das Ergebnis in das Ursprungscanvas zurückkopieren.

Medium 29. Apr 2016 09:26

AW: canvas.rectangle transparent?
 
Ahhhh! Der Text steht schon drin! Da war mein Denkfehler. Dann hast du eigentlich keine Chance "nur den Hintergrund" zu ändern, weil du ja überhaupt keine Infos darüber erhalten kannst, was jetzt genau Hintergrund und was Text ist. Da fallen mir fast nur Methoden wie Helligkeit und Kontrast in dem betreffenden Bereich so zu ändern, dass es die Lesbarkeit evtl. etwas verbessert. Aber sauber den "Hintergrund" zu ändern, da wäre wohl der noch einfachste Weg den Text via OCR erkennen, das Rechteck übermalen und den Text selbst wieder drauf schreiben. Aber wenn der Text schon für Menschen schlecht lesbar ist, dann wird ein OCR Algo auch seine Mühen damit haben.

Wenn der Text nicht zu dünn ist, und eine recht gut definierte Farbe hat, die im Hintergrund sonst nicht vorkommt, bliebe halt noch alles was nicht textfarbig ist anders einzufärben, via Pixels[] dann im Zweifelsfall. Und dann hoffen, dass der Text dabei nicht zu arg ausfranst. Je nach dem mit was der geschrieben wurde, und woher und in welchem Format das Bild generell kommt. (Ich denke da an Farbvariationen die durch Kompression eingeführt werden usw.)

Aber wirklich "hübsch und sauber" wird das leider nie werden.

uligerhardt 29. Apr 2016 10:28

AW: canvas.rectangle transparent?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Vielleicht mit AlphaBlending? Etwa so:
Anhang 45236
Hier ist ein Artikel dazu: https://parnassus.co/transparent-gra...re-gdi-part-1/
Ich hab mir "damals" das draus gebastelt:
Delphi-Quellcode:
// AlphaBlendRect: Zeichnet das Rechteck ARect alphageblendet in der Farbe AColor
// und der Intensität AIntensity (0 = durchsichtig, 255 = deckend) auf den übergebenen DC.
procedure AlphaBlendRect(DC: HDC; const ARect: TRect; AColor: TColor; AIntensity: Byte);
var
  Bitmap: TBitmap;
  BlendParams: TBlendFunction;
  rClip, rBlend: TRect;

  function GetBlendColor: TRGBQuad;

    function PreMult(b: Byte): Byte;
    begin
      Result := (b * AIntensity) div $FF;
    end;

  var
    cr: TColorRef;
  begin
    cr := ColorToRGB(AColor);
    Result.rgbBlue := PreMult(GetBValue(cr));
    Result.rgbGreen := PreMult(GetGValue(cr));
    Result.rgbRed := PreMult(GetRValue(cr));
    Result.rgbReserved := AIntensity;
  end;

begin
  GetClipBox(DC, rClip);
  //NormalizeRect(rClip); // Kannst du ignorieren
  rBlend := ARect;
  //NormalizeRect(rBlend); // Kannst du ignorieren

  if not Windows.IntersectRect(rBlend, rClip, rBlend) then
    Exit;

  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.SetSize(1, 1);
    PRGBQuad(Bitmap.ScanLine[0])^ := GetBlendColor;

    BlendParams.BlendOp := AC_SRC_OVER;
    BlendParams.BlendFlags := 0;
    BlendParams.SourceConstantAlpha := $FF;
    BlendParams.AlphaFormat := AC_SRC_ALPHA;

    Windows.AlphaBlend(
      DC, rBlend.Left, rBlend.Top, RectWidth(rBlend), RectHeight(rBlend),
      Bitmap.Canvas.Handle, 0, 0, 1, 1,
      BlendParams);
  finally
    Bitmap.Free;
  end;
end;

stahli 29. Apr 2016 12:19

AW: canvas.rectangle transparent?
 
Ich weiß nicht, ob AlphaBlend dafür tauglich ist. Das Verfahren kann ja auch nicht zwischen Vorder- und Hintergrund unterscheiden.

Evtl. bringt Dich die Helligkeit weiter.
http://www.delphipraxis.net/88775-fa...eraendern.html
Ggf. könntest Du Pixel, die x Helligkeitswerte unter oder über der Texthelligkeit liegen, durch eine neue Hintergrundfarbe ersetzen.

uligerhardt 29. Apr 2016 12:24

AW: canvas.rectangle transparent?
 
Zitat:

Zitat von stahli (Beitrag 1337077)
Ich weiß nicht, ob AlphaBlend dafür tauglich ist. Das Verfahren kann ja auch nicht zwischen Vorder- und Hintergrund unterscheiden.

Muss es ja auch nicht. Dem OP geht's darum, den Bereich um den Text hervorzuheben. Das ginge damit IMHO sehr gut.

Neutral General 29. Apr 2016 12:33

AW: canvas.rectangle transparent?
 
Zitat:

Zitat von uligerhardt (Beitrag 1337079)
Zitat:

Zitat von stahli (Beitrag 1337077)
Ich weiß nicht, ob AlphaBlend dafür tauglich ist. Das Verfahren kann ja auch nicht zwischen Vorder- und Hintergrund unterscheiden.

Muss es ja auch nicht. Dem OP geht's darum, den Bereich um den Text hervorzuheben. Das ginge damit IMHO sehr gut.

Und wie soll das gehen? Wenn du über den ganzen Bereich transparent drüber zeichnest ist auch der Text betroffen und verfärbt sich.

uligerhardt 29. Apr 2016 12:41

AW: canvas.rectangle transparent?
 
Zitat:

Zitat von Neutral General (Beitrag 1337080)
Ich weiß nicht, ob AlphaBlend dafür tauglich ist. Das Verfahren kann ja auch nicht zwischen Vorder- und Hintergrund unterscheiden.

Kommt wahrscheinlich auf die Farbgebung der Vorlage an. Im Explorer geht's ja z.B. Ich würde es einfach mal ausprobieren. :mrgreen:
@stahli, kannst du mal ein Beispiel zeigen?

stahli 29. Apr 2016 12:45

AW: canvas.rectangle transparent?
 
Nein, dafür habe ich kein Beispiel.
War nur eine Überlegung.

(Aber das Abdunkeln und Aufhellen von Farben an sich funktioniert super.)

uligerhardt 29. Apr 2016 13:13

AW: canvas.rectangle transparent?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von uligerhardt (Beitrag 1337082)
@stahli, kannst du mal ein Beispiel zeigen?

Zitat:

Zitat von stahli (Beitrag 1337083)
Nein, dafür habe ich kein Beispiel.

Dich hab ich doch auch gar nicht gemeint, sondern Mattze.
Sorry für die Verwirrung - ich kann nicht lesen. :wall: :mrgreen:


EDIT: Ich musste es jetzt doch mal ausprobieren. Ich finde, das schaut ganz gut aus. Das hängt aber natürlich vom Hintergrund ab.
Anhang 45240
Delphi-Quellcode:
// AlphaBlendRect: Zeichnet das Rechteck ARect alphageblendet in der Farbe AColor
// und der Intensität AIntensity (0 = durchsichtig, 255 = deckend) auf den übergebenen DC.
procedure AlphaBlendRect(DC: HDC; const ARect: TRect; AColor: TColor; AIntensity: Byte);
var
  Bitmap: TBitmap;
  BlendParams: TBlendFunction;
  rClip, rBlend: TRect;

  function GetBlendColor: TRGBQuad;

    function PreMult(b: Byte): Byte;
    begin
      Result := (b * AIntensity) div $FF;
    end;

  var
    cr: TColorRef;
  begin
    cr := ColorToRGB(AColor);
    Result.rgbBlue := PreMult(GetBValue(cr));
    Result.rgbGreen := PreMult(GetGValue(cr));
    Result.rgbRed := PreMult(GetRValue(cr));
    Result.rgbReserved := AIntensity;
  end;

begin
  GetClipBox(DC, rClip);
  if not Windows.IntersectRect(rBlend, rClip, ARect) then
    Exit;

  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.SetSize(1, 1);
    PRGBQuad(Bitmap.ScanLine[0])^ := GetBlendColor;

    BlendParams.BlendOp := AC_SRC_OVER;
    BlendParams.BlendFlags := 0;
    BlendParams.SourceConstantAlpha := $FF;
    BlendParams.AlphaFormat := AC_SRC_ALPHA;

    Windows.AlphaBlend(
      DC, rBlend.Left, rBlend.Top, rBlend.Right - rBlend.Left, rBlend.Bottom - rBlend.Top,
      Bitmap.Canvas.Handle, 0, 0, 1, 1,
      BlendParams);
  finally
    Bitmap.Free;
  end;
end;

procedure TForm2.PaintBox1Paint(Sender: TObject);
var
  r: TRect;
begin
  PaintBox1.Canvas.Brush.Color := clWhite;
  PaintBox1.Canvas.Pen.Color := clBlack;
  PaintBox1.Canvas.FillRect(ClientRect);
  PaintBox1.Canvas.TextOut(100, 100, 'Alarma! Ohne Highlighting');
  PaintBox1.Canvas.TextOut(100, 200, 'Alarma! Mit Highlighting');
  r := Bounds(50, 150, 200, 100);
  AlphaBlendRect(PaintBox1.Canvas.Handle, r, clRed, 50);
  PaintBox1.Canvas.Pen.Color := clRed;
  PaintBox1.Canvas.Brush.Style := bsClear;
  PaintBox1.Canvas.Rectangle(r);
end;

Mattze 29. Apr 2016 14:52

AW: canvas.rectangle transparent?
 
Hallo,

vielen Dank für Eure Ratschläge.
Alphablending hatte ich auch mal ausprobiert. Sieht hübsch aus, hat aber den Nachteil, dass es auch den Text langsam "ausblendet" (abhängig von der Intensität).

Ich habe das jetzt auf eine ganz einfache Weise gelöst, musste dazu aber doch eine Bitmap zu Hilfe nehmen:

Delphi-Quellcode:
    bmp:=Tbitmap.Create;
    try
      bmp.Height:=itemrect.Bottom-itemrect.Top;
      bmp.Width:=itemrect.Right-itemrect.Left;
      bmp.Canvas.CopyRect(rect(0,0,bmp.width,bmp.Height),targetcanvas,itemrect);
      bmp.Transparent:=true;
      bmp.TransparentColor:=clWhite;
      targetcanvas.Brush.Color:=clGradientActiveCaption;
      targetcanvas.fillrect(itemrect); //falls Rahmen gewünscht: Rectangle(itemrect);
      targetcanvas.Draw(itemrect.Left,itemrect.Top,bmp);
    finally
      bmp.Free
    end;
Kurzbeschreibung: gewünschtes Rechteck des Canvas auf eine Bitmap kopieren.
Canvas färben.
Bitmap transparent zurück auf den Canvas schreiben.

Geht gut und schnell.

Gruß
Mattze

Blup 29. Apr 2016 16:29

AW: canvas.rectangle transparent?
 
Hallo,

Ich glaube hier besteht noch Informationsbedarf, was den ein Canvas eigentlich ist.
Der Canvas repräsentiert ein reines Ausgabegerät, das mit Zeichenbefehlen gesteuert wird.
Das heißt das nachträgliche Lesen von dem was ausgegeben wurde, ist eigentlich nicht vorgesehen.
Ein Beispiel wäre ein Plotter, der jeden Zeichenbefehl sofort in Stiftbewegungen umsetzt.

Die Grafikkarte hat natürlich eigenen Speicher, in dem das Ergebnis der Zeichenbefehle vor der Ausgabe zwischengespeichert wird.
Aber dieser Speicher muss nicht direkt vom Hauptprozessor erreichbar sein. Die Zeichenbefehle können auch von Prozessoren auf der Grafikkarte ausgeführt werden.
Selbst wenn die Grafikkarte die Möglichkeit hat, Teile ihres Speichers in der Adressraum des Hauptprozessors einzublenden,
so sind das Adressbereiche auf die nur der Grafikkartentreiber zugreifen kann.

Um trotzdem die Möglichkeit zu haben auf den Grafikspeichers zuzugreifen, wurden geräteabhängige Bitmap geschaffen.
Mit der BitBlt-Funktion können Teile des Grafikspeichers in diese Bitmaps und von dort wieder zurück kopiert werden.

Allerdings liegen die Daten dort in einem Format vor, daß der Grafiktreiber bestimmt.
Für Zeichenbefehle auf diese Bitmaps kann ein gerätekompatiblen Speichercancvas erzeugt werden.

Um die Daten direkt im Speicher bearbeiten zu können, muss diese erst in ein geräteunabhängiges Standardformat überführt werden.
Für diese Formate ist genau definiert wie die Daten im Speicher abgelegt sind.

Im Format pf24Bit werden für jedes Pixel 3 Byte abgelegt, jedes Byte steht für einen Farbanteil in der Reihenfolge Blau, Grün, Rot.
Delphi-Quellcode:
type
  TBGR =
    B: Byte;
    G: Byte;
    R: Byte;
  end;
Im Gegensatz dazu können in TColor RGB-Werte gespeichert werden, das heißt Blau und Rot sind in der Reihenfolge getauscht.

Um auf das Problem zurück zu kommmen, mir scheint die beste Lösung zu sein:
- Bitmap der entsprechenden Größe erzeugen
- den Bildauschnitt kopieren
- Bitmap verändern
- Bitmap wieder auf den ursprüngliche Canvas zeichnen

Wie ich sehe ist das auch deine Lösung.


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:53 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz