Zeichnen TImage falsche Farbe bei Hintergrundbild

Für Probleme bezüglich Grafik, Audio, GL, ACS, ...
vazili_Zaitzef
Beiträge: 43
Registriert: Do 6. Dez 2012, 21:55
OS, Lazarus, FPC: Win7 64 (L 1.6 FPC 3.0.0)
CPU-Target: 64Bit

Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von vazili_Zaitzef »

In einem TImage wurde ein Hintergrundbild geladen. Jetzt soll ein Rechteck gezeichnet werden. Hier ist es so, dass das Rechteck immer grau ist.

Wenn ich selbiges noch einmal mache one ein Hintergrundbild zu laden ist die Rechteckfarbe richtig. :cry:

Ich benötige beides im gleiche Image, da es später abgespeichert werden soll und das Rechteck etwas markiert.
Hatte dieses Problem schon mal jemand und wenn ja, gibt es hierfür eine Lösung?

Danke und Beste Grüße
Vazili


Funktionen die benutzt werden:


Code: Alles auswählen

 
 
 
 
 
procedure Image_Rechteck_Zeichnen(Form_Name,Image_Name,Pinselfarbe{CLWhite}:String;Linienstaerke,X_Start,Y_Start,X_Laenge,Y_Laenge:Double);
var
  Form                  : TForm;
  Image_Componente      : TComponent;
  I                     : Integer;
begin
  //Form und Componenten Definieren
  Form                                      := Application.FindComponent(Form_Name) as TForm;
  Image_Componente                          := Form.findComponent(Image_Name) as TImage;
  I                                         := 0;
 
  if Pinselfarbe = 'CLBlue' then
      Pinselfarbe := 'CLRED';
 
 
  (Image_Componente as TImage).canvas.AutoRedraw := True;
 
  (Image_Componente as TImage).Canvas.pen.Width   := round(Linienstaerke);
  (Image_Componente as TImage).Canvas.Pen.Color   := StringToColor(Pinselfarbe);
  (Image_Componente as TImage).Canvas.Brush.Color := StringToColor(Pinselfarbe);
  (Image_Componente as TImage).Picture.Bitmap.Transparent:=True;
  //(Image_Componente as TImage).Canvas.Clear;
  //(Image_Componente as TImage).Canvas.Clear;
 
  while I <> Linienstaerke do
       begin
         (Image_Componente as TImage).Canvas.FrameRect(round(X_Start),round(Y_Start),round(X_Laenge),round(Y_Laenge));
         X_Start  := X_Start  + 1;
         Y_Start  := Y_Start  + 1;
         X_Laenge := X_Laenge - 1;
         Y_Laenge := Y_Laenge - 1;
         I := I + 1;
       end;
end;
 
Function Bild_in_Image_Laden(Form_Name,Image_Name,Datei_mit_Pfad:String;Grafik_aus_Zwischenablage:Boolean):String;
var
  Form                  : TForm;
  Scrollbox_Componente  : TComponent;
  Image_Componente      : TComponent;
  endung                : STring;
begin
  Form                      := Application.FindComponent(Form_Name) as TForm;
  Image_Componente          := Form.findComponent(Image_Name) as TImage;
  (Form.FindComponent(Image_Name) as TImage).AutoSize    := True;
  (Form.FindComponent(Image_Name) as TImage).Proportional:= True;
 
  If Grafik_aus_Zwischenablage = False then
    begin
    if Datei_mit_Pfad[length(Datei_mit_Pfad)-3] = '.' then
         endung := copy(Datei_mit_Pfad,length(Datei_mit_Pfad)-2,length(Datei_mit_Pfad));
 
    if Datei_mit_Pfad[length(Datei_mit_Pfad)-4] = '.' then
         endung := copy(Datei_mit_Pfad,length(Datei_mit_Pfad)-3,length(Datei_mit_Pfad));
 
    case uppercase(endung) of
      'PNG': (Form.FindComponent(Image_Name) as TImage).Picture.PNG.LoadFromFile(Datei_mit_Pfad);
      'BMP': (Form.FindComponent(Image_Name) as TImage).Picture.Bitmap.LoadFromFile(Datei_mit_Pfad);
      'DIB': (Form.FindComponent(Image_Name) as TImage).Picture.Bitmap.LoadFromFile(Datei_mit_Pfad);
      'JPG': (Form.FindComponent(Image_Name) as TImage).Picture.Jpeg.LoadFromFile(Datei_mit_Pfad);
      'JPEG': (Form.FindComponent(Image_Name) as TImage).Picture.Jpeg.LoadFromFile(Datei_mit_Pfad);
      'JPE': (Form.FindComponent(Image_Name) as TImage).Picture.Jpeg.LoadFromFile(Datei_mit_Pfad);
      'JFIF': (Form.FindComponent(Image_Name) as TImage).Picture.Jpeg.LoadFromFile(Datei_mit_Pfad);
    else endung := 'ERROR';
      end;
    if endung = 'ERROR' then
      begin
        showmessage('Keine Bilddatei ausgewählt!');
        exit;
      end;
    end
  else
  begin
    (Form.FindComponent(Image_Name) as TImage).Picture.Bitmap.LoadFromClipboardFormat(CF_BITMAP);
  end;
 
  (Form.FindComponent(Image_Name) as TImage).AutoSize    := False;
  (Form.FindComponent(Image_Name) as TImage).Proportional:= False;
  Result := 'OK';
end;

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von theo »

Warum machst du denn das alles so kompliziert?

Das ganze FindComponent und Casting Zeug kannst du dir sparen, wenn du statt "Image_Name" und "Form_Name" einfach das Objekt übergibst.

Code: Alles auswählen

(AImage:TImage; AForm:TForm,...)


Warum verwendest du Prozeduren und Funktionen und nicht Methoden?

Der ganze "case uppercase(endung) of" Block ist überflüssig.

Code: Alles auswählen

AImage.Picture.LoadFromFile(Datei_mit_Pfad);

reicht.

vazili_Zaitzef
Beiträge: 43
Registriert: Do 6. Dez 2012, 21:55
OS, Lazarus, FPC: Win7 64 (L 1.6 FPC 3.0.0)
CPU-Target: 64Bit

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von vazili_Zaitzef »

Hi,

ich habe mir das Programmieren selber beigebracht. Daher war mir das nicht bewusst, dass es so einfach geht. :shock:
Habe das geändert.
Hast Du ein gutes Tutorial für Methoden? Dann kann ich das ändern und benutzen. :mrgreen:

Allerdings besteht immer noch mein Problem. :oops:

Sobald eine Bilddatei im Hintergrund geladen ist, wird die Farbe vom Rechteck abgeändert nach grau.
Wei kann man das Abstellen?

Danke für Deine Hilfe :!:

Code: Alles auswählen

procedure Image_Rechteck_Zeichnen(AImage:TImage;Pinselfarbe{CLWhite}:String;Linienstaerke,X_Start,Y_Start,X_Laenge,Y_Laenge:Double);
var
  I                     : Integer;
begin
  I                                         := 0;
 
  if Pinselfarbe = 'CLBlue' then
      Pinselfarbe := 'CLRED';
 
 
  AImage.Canvas.AutoRedraw  := True;
  AImage.Canvas.pen.Width   := round(Linienstaerke);
  AImage.Canvas.Pen.Color   := StringToColor(Pinselfarbe);
  AImage.Canvas.Brush.Color := StringToColor(Pinselfarbe);
 
  while I <> Linienstaerke do
       begin
         AImage.Canvas.FrameRect(round(X_Start),round(Y_Start),round(X_Laenge),round(Y_Laenge));
         X_Start  := X_Start  + 1;
         Y_Start  := Y_Start  + 1;
         X_Laenge := X_Laenge - 1;
         Y_Laenge := Y_Laenge - 1;
         I := I + 1;
       end;
end
 
 
Function Bild_in_Image_Laden(AImage:TImage;Datei_mit_Pfad:String;Grafik_aus_Zwischenablage:Boolean):String;
begin
  AImage.AutoSize := True;
  AImage.Proportional := True;
  If Grafik_aus_Zwischenablage = False then
    begin
      AImage.Picture.LoadFromFile(Datei_mit_Pfad);
    end
    else
    begin
      AImage.Picture.LoadFromClipboardFormat(CF_BITMAP);
    end;
  AImage.AutoSize := False;
  AImage.Proportional:= False;
  Result :='';
end;

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von wp_xyz »

Im Anhang habe ich dein Programm so umgeschrieben, dass es auch theo gefallen sollte:

  • Deine beiden Routinen sind nun Methoden der Klasse TForm1.
  • Die Routine, die das Rechteck malt, ist nicht mehr jederzeit aufrufbar, sondern wird vom OnPaint-Ereignis des Formulars aufgerufen. Das deshalb, weil alles was du außerhalb dieses Ereignisse ausgibst, jederzeit zunichte gemacht werden kann, wenn das Betriebssystem meint, das Formular neu zeichnen zu müssen. Woher soll das Formular nun wissen, dass es das Rechteck ins Bild einblenden soll? Dafür gibt es das Ereignis OnPaint: Immer wenn das Bild neu gezeichnet werden muss, wird hinterher der Handler von OnPaint aufgerufen. Hier kannst du nun deinen eigenen Code platzieren. Da in meinem Beispiel das Bild einmal mit, einmal ohne Rechteck ausgegeben werden soll, gibt es eine Checkbox dafür. Das Setzen des Häkchens ruft nur Image1.Invalidate auf - was soviel bedeutet wie: "Das Bild ist nicht mehr richtig dargestellt und muss bei der nächsten Gelegenheit neu gezeichnet werden".
  • Die Farben werden nicht als String übergeben, sondern direkt als die entsprechenden TColor-Variablen.
  • Die Koordinaten des Rechtecks sind keine Double-Werte, sondern einfache Integer - du kannst auf dem Canvas nur an ganzzahligen Pixel-Koordinaten malen. Dadurch entfällt das Runden.
  • Das Rechteck selbst male ich nicht per FrameRect, sondern einfach per Rectangle. FrameRect ist gedacht für spezielle Rahmen um Controls etc. Die allgemeine Rechteckrouine ist Rectangle, oder FillRect (letzteres ohne Rand).
  • Das mit dem Hinundherschalten von Image1.AutoSize und Proportional beim Laden des Bilds habe ich nicht verstanden.
Ach ja, deinen Fehler konnte ich nur bei der 1.Ausgabe, bevor ich meine Änderungen eingebracht hatte, sehen, habe es aber nicht gleich verfolgt. Und jetzt ist alles bestens... Ich kann daher nicht näher sagen, was das Problem war.
Dateianhänge
Malen_im_Image.zip
(90.11 KiB) 138-mal heruntergeladen

vazili_Zaitzef
Beiträge: 43
Registriert: Do 6. Dez 2012, 21:55
OS, Lazarus, FPC: Win7 64 (L 1.6 FPC 3.0.0)
CPU-Target: 64Bit

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von vazili_Zaitzef »

Vielen Dank für die ausführliche Antwort. :)))))
So einiges verstehe ich nun sehr viel besser.

Nur warum die beiden Funktionen nun Methoden sind verstehe ich noch nicht so ganz.

Also was ich vor habe ist, ein kleines Tool zu bauen, in dem ich Bilder Zoomen, verschieben und mit der Maus Bereiche markieren kann. Man könnte sagen ein sehr schlankes Paint :mrgreen:
Das Ganze soll so sein, das man das in einer eigenen Unit hat, damit man diese später auch für andere Programme verwenden kann.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von wp_xyz »

vazili_Zaitzef hat geschrieben:Nur warum die beiden Funktionen nun Methoden sind verstehe ich noch nicht so ganz.

Ich kann jetzt hier keine Abhandlung über objekt-orientierte Programmierung schreiben. Schau dir mal dieses an: https://www.delphi-treff.de/object-pasc ... d-objekte/

Deine ursprünglichen Funktione waren immer und überall, in jedem Kontext und aus jeder Unit heraus aufrufbar. Das muss nicht passend sein, und daher kann es vor allem bei großen Programmen leicht zu Fehlern kommen. Stell dir vor, das Formular benötigt aus irgendeinem Grund ab einem bestimmten Zeitpunkt im Programmablauf das Image in einer vorgegebenen Größe, und nun wird dein "Bild_in_Image_laden" aufgerufen: Wenn die ursprünglichen Zeilen mit dem AutoSize noch drinnen sind, geht die vorher eingestellte Bildgröße verloren.

Sind die Funktionen Methoden der Klasse TForm1, noch dazu als "private" deklariert wie in dem Beispiel, dann können sie nur aus der Klasse TForm1 heraus aufgerufen werden. Im Idealfall ist die Implementierung einer Klasse wesentlich übersichtlicher als die eines Riesenprogramms. Daher besteht die Hoffnung, dass die Klasse TForm1 "weiß", wann sie seine Methoden aufrufen darf, so dass die Fehleranfälligkeit reduziert wird. Ein weiterer Vorteil ist, dass man von einer Klasse andere Klassen ableiten kann, die dann automatisch die Methoden der ursprünglichen Klasse erben. TForm1 z.B. ist von TForm abgeleitet, und TForm wiederum von TCustomForm, und TCustomForm hat eine Methode Show, mit der das Formular angezeigt wird, Wegen dieser Abstammungshierarchie hat auch TForm1 diese Methode ohne dass man dafür etwas neu programmieren muss.

Ob eine Funktion eine Methode ist oder eine klassische Funktion erkennst du an der Deklaration. Eine Methode muss im Kontext einer Klasse deklariert sein, und bei der Implementierung muss der Name der Klasse dem Methodennamen vorangestellt werden, also

Code: Alles auswählen

type
  TForm1 = class(TForm)   // TForm1 ist von TForm abgeleitet, d.h. kann alles was TForm auch kann
    procedure BildZeigen_Methode(Dateiname: String);   // das ist eine Methode
  end;
 
procedure TForm1.BildZeigen_Methode(Dateiname: String);
begin
  Image1.Picture.LoadfromFile(Dateiname);
end;

Eine klassische Prozedur/Funktion dagegen kann ohne den "class"-Rahmen deklariert werden, benötigt aber entsprechende Parameter, wenn sie auf Klassen-Instanzen zugreifen will

Code: Alles auswählen

procedure BildZeigen_klassich(AImage: TImage; Dateiname: String);    // das ist eine klassiche Prozedur
begin
  AImage.Picture.LoadfromFile(Dateiname);
end;

Sicher könnte man das kleine Demoprojekt auch mit den klassischen Funktionen realisieren. Aber gerade als Anfänger oder Autodidakt sollte man sich angewöhnen gleich mit den richtigen Werkzeugen zu arbeiten.

vazili_Zaitzef
Beiträge: 43
Registriert: Do 6. Dez 2012, 21:55
OS, Lazarus, FPC: Win7 64 (L 1.6 FPC 3.0.0)
CPU-Target: 64Bit

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von vazili_Zaitzef »

Da verstehe ich es jetzt ein wenig besser.
Danke für den Link, den werde ich mir ausführlich zu Gemüte führen :)

Werde das Gelernte gleich mal ausprobieren :)

vazili_Zaitzef
Beiträge: 43
Registriert: Do 6. Dez 2012, 21:55
OS, Lazarus, FPC: Win7 64 (L 1.6 FPC 3.0.0)
CPU-Target: 64Bit

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von vazili_Zaitzef »

Hi,

ich habe es jetzt versucht und bekomme immer eine Fehlermeldung...
Error: Only class methods, class properties and class variables can be accessed in calss methods
bzw.
Only class methods, class properties and class variables can be referred with class references. :?: :?: :?:


Code: Alles auswählen

unit Unit2;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Classes, SysUtils, Dialogs, Forms, FileUtil, Controls, Graphics, ExtCtrls;
 
 
 
 
type
TBildbearbeitung = Class(TForm)
    procedure ABC(A:String);
//private
  //FFarbe:String;
//public
 
end;
 
implementation
 
 
 
procedure TBildbearbeitung.ABC(A:String);
begin
 
  //showmessage('MELDUNG!');
end;
 
 
end.


und in Unit1 habe ich einfach einen button aufgemacht und dies versucht:

Code: Alles auswählen

procedure TForm1.Button3Click(Sender: TObject);
begin
  TBildbearbeitung.ABC('asdf');
end

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von Mathias »

So musste es gehen.

Code: Alles auswählen

type
  TBildbearbeitung = Class(TForm)
    class  procedure ABC(A:String);


Oder wen es sich, ich nehme es bei dir an um ein Form handelt, könnte es auch so gehen:

Code: Alles auswählen

Bildbearbeitung.ABC('asdf');

Mann ruft normalerweise den Bezeichner einer Form auf und nicht direkt den Typ.

Hast du evtl.

Code: Alles auswählen

var
  Bildbearbeitung : TBildbearbeitung

aus der Unit2 gelöscht ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von wp_xyz »

vazili_Zaitzef hat geschrieben:

Code: Alles auswählen

procedure TForm1.Button3Click(Sender: TObject);
begin
  TBildbearbeitung.ABC('asdf');
end

Das ist ein kleiner, aber wichtiger Unterschied. Wenn du "TBildbearbeitung.ABC" aufrufst, meint der Compiler, du rufst eine "Klassenmethode" auf, das ist eine Methode, die für die Klasse allgemein gilt und keine Instanz benötigt. In der Regel arbeitet man aber mit Instanzen (= Variablen des Typs der Klasse). Irgendwo müsste in deinem Code noch die Deklaration der Instanz stehen:

Code: Alles auswählen

var
  Bildbearbeitung: TBildbearbeitung

und irgendwo muss die Instanz auch noch erzeugt werden, die alleinige Deklaration reicht nicht:

Code: Alles auswählen

Bildbearbeitung := TBildbearbeitung.Create(...);
//oder
Application.CreateForm(TBildbearbeitung, Bildbearbeitung);   

Wenn du dann eine Methode der Bildbearbeitung beim Button-Click im anderen Formular (Form1) aufrufen willst, dann schreibst du

Code: Alles auswählen

Bildbearbeitung.ABC('asdf')// ohne das "T"

Falls dich verwirrt, was das mit Klasse und Instanz soll: TBildbearbeitung beschreibt ganz allgemein ein Fenster, in dem du ein Bild bearbeiten kann - das ist die Klasse. BildBearbeitung (ohne das "T") ist ein konkretes Exemplar dieses speziellen Fensters, du kannst es natürlich auch ganz anders nennen (z.B. nur B - dann wäre die Deklaration: var B: TBildBearbeitung) - das ist die Instanz. Und falls du z.B. ein zweites Bildbearbeitungsfenster brauchst, würdest du folgendes tun:

Code: Alles auswählen

var
  Bildbearbeitung2: TBildbearbeitung;
begin
  Bildbearbeitung2 := TBildbearbeitung.Create(...);

P.S. die Pünktchen im Create habe ich noch nicht erklärt: Hier steht das Objekt, das die erzeugte Instanz wieder freigibt, "zerstört", der sog. "Owner". In der Regel ist das "self" - das ist das Objekt, das Create aufruft. Oder Application, also die Anwendung. Aus diesem Grund musst du dich um die Freigabe der LCL-Klassen in der Regel nicht selbst kümmern. Nur wenn du als Owner nil einträgst, musst du selbst die Instanz freigeben (also .Free oder .Destroy aufrufen).

vazili_Zaitzef
Beiträge: 43
Registriert: Do 6. Dez 2012, 21:55
OS, Lazarus, FPC: Win7 64 (L 1.6 FPC 3.0.0)
CPU-Target: 64Bit

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von vazili_Zaitzef »

Hi,

danke :)
Mein Fehler war einfach, dass ich die Variable nicht deklariert habe. :oops:

Ich habe vor in meinem Image zu zeichnen auch mit einer Vorschau. Über welche Funktion kann ich „on the fly“ die Mausposition abfragen und vor allem, wie kann ich die Vorschau von dem Rahmen einblenden. Muss ich das Image immer wieder "Zeichnen" oder gibt es da etwas vordefiniertes? :?: (Wie in Paint ein Rechteck erstellen)

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von theo »

vazili_Zaitzef hat geschrieben:Ich habe vor in meinem Image zu zeichnen auch mit einer Vorschau. Über welche Funktion kann ich „on the fly“ die Mausposition abfragen


Dafür eignen sich die dafür vorgesehenen Ereignisse wie OnMouseMove etc.

vazili_Zaitzef
Beiträge: 43
Registriert: Do 6. Dez 2012, 21:55
OS, Lazarus, FPC: Win7 64 (L 1.6 FPC 3.0.0)
CPU-Target: 64Bit

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von vazili_Zaitzef »

Hi,

war leider beruflich etwas länger weg. Aber bald ist Urlaub und dann hab ich mehr Zeit.
Ich muss noch eine doofe Frage zu den Klassen los werden.

Ich habe eine Klasse mit 2 Funktionen.
Nun möchte ich aus der einen Funktion die andere aufrufen. Da bekomm ich immer ne Meldung:
Only class methods, class properties and class variables can be accessed in class methods.

Wie bekomm ich die weg?

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von theo »

vazili_Zaitzef hat geschrieben:Wie bekomm ich die weg?


Im Allgemeinen bekommt man Fehlermeldungen weg, indem man den Programmierfehler beseitigt.
Was das in deinem Fall sein könnte, ist schwer zu sagen, ohne den Code zu sehen.

vazili_Zaitzef
Beiträge: 43
Registriert: Do 6. Dez 2012, 21:55
OS, Lazarus, FPC: Win7 64 (L 1.6 FPC 3.0.0)
CPU-Target: 64Bit

Re: Zeichnen TImage falsche Farbe bei Hintergrundbild

Beitrag von vazili_Zaitzef »

Das passiert mir eigentlich immer....
Hiermal ein kleines Beispiel, um es einfach zu halten:

Unit 1

Code: Alles auswählen

 
procedure TForm1.Button3Click(Sender: TObject);
begin
  TVersuch.Zweitefunction();
end;
 


Unit2

Code: Alles auswählen

 
implementation
type
  TVersuch = class
    private
    public
    procedure Erstefunction(Variablen_uebergabe:string);
    procedure Zweitefunction();
  end;   
 
 
procedure TVersuch.Erstefunction(Variablen_uebergabe:string);
begin
 showmessage(Variablen_uebergabe);
end;
 
procedure TVersuch.Zweitefunction();
begin
  Erstefunction('ABC');
end;


Und schon ist der Fehler da...

Antworten