FPC <> Delphi inkompatibilität

Für Fehler in Lazarus, um diese von anderen verifizieren zu lassen.
Antworten
Benutzeravatar
theo
Beiträge: 10497
Registriert: Mo 11. Sep 2006, 19:01

FPC <> Delphi inkompatibilität

Beitrag von theo »

Ich hab da mal ne Frage an die Experten.
Wir ihr ja wisst, bin ich dabei Image-Formate zu portieren.
Dabei bin ich (nach längerem verzweifeltem Suchen) auf ein unerwartetes Phänomen gestossen.
Auf's Minimum reduziert sieht's so aus:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var a,b:Byte;
begin
{.$Q+}
a:=91;
b:=84;
Edit1.text:=(intToStr(a + b - 256));    // Delphi gibt -81, FPC gibt 4294967215
end;


Delphi/Kylix gibt immer -81 zurück, was man auch erwarten würde.
FPC gibt 4294967215 resp. mit {$Q+} einen EIntOverflow.
(FPC 2.0.4 [2006/08/20] for i386)

Ist das ein Bug oder hat das seine Berechtigung?
Wenn letzteres, wie kann ich das "richtige" Resultat bekommen, ohne die Datentypen ändern zu müssen?
Vielen Dank!

P.S. hab's soeben rausgefunden:

Edit1.text:=(intToStr(integer(a + b - 256)));

Aber ich lasse den Beitrag trotzdem stehen, als Warnung. ;-)
Man muss schon ziemlich aufpassen...


------------------

P.S.2 @monta

Mir ist schon oft aufgefallen, dass nach zweimaligem editieren die Sourcecodes "gescrambled" werden.
Ich setzte die dann jeweils wieder neu ein.
Sieht so aus

procedure TForm1.Button1Click%u28Sender%u3a TObject%u29;
var a,b%u3aByte;
begin
%u7b.$Q+%u7d
a%u3a=91;
b%u3a=84;
Edit1.text%u3a=%u28intToStr%u28a + b - 256%u29%u29; // Delphi gibt -81, FPC gibt 4294967215
end;

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

Beitrag von theo »

So, nachdem dieser Bug nun behoben ist, darf ich jetzt im Schweisse meines Angesichts ;-)
verkünden, dass OPBitmap nun nativ ohne X-Server folgende Formate beherrscht:

BMP R/W
GIF R/W
JPEG R/W
PNG R/ W folgt.

Kleine Demo: das Forumlogo, geladen, mit Kommentar versehen und mit leicht stärkerer Kompression als das Original wieder gespeichert.
GIF von JPEG konvertiert und mit Transparenz versehen.

Der "End Programmer" Code dies zu tun sieht so aus:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var jp:TJPEGImage;
gif:TGIFImage;
begin
jp:=TJPEGImage.Create;
jp.LoadFromFile('/home/theo/logo.jpg');
jp.Comment:='Hallo Lazarusforum';
jp.SaveToFile('/home/theo/logoop.jpg');
 
gif:=TGIFImage.Create;
gif.Assign(jp);
gif.PixelFormat:=pf8bit; //native optimierte Farbquantisierung, siehe Resultat.
gif.TransparentColor:=gif.Pixels[1,1]
gif.SaveToFile('/home/theo/logoop.gif');
gif.free;
 
AssignOpBitmapToBitmap(jp, Image1.Picture.Bitmap); // Auf den Screen bringen.
jp.free;
end;
Dateianhänge
logoop.jpg
logoop.gif
(24.65 KiB) 14-mal heruntergeladen

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

Sieht ja wirklich interessant aus, was man damit alles machen kann.
Auch wenn ich bis eben nichts von Jpeg-Kommentaren wusste, aber es steht wirklich drin, Wordpad hats gezeigt ;).

Da bin ich mal gespannt, wanns ne offizielle Version gibt.

--
zu P.S.2: ist mir noch nicht aufgefallen, und das problem hat ich trotz mehrfachem editieren auch noch nie, gibts nen genauen weg, das zu reproduzieren? (Eventuell per Pn)

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

Beitrag von theo »

monta hat geschrieben:Sieht ja wirklich interessant aus, was man damit alles machen kann.
Auch wenn ich bis eben nichts von Jpeg-Kommentaren wusste, aber es steht wirklich drin, Wordpad hats gezeigt ;).

Freut mich, dass es klappt. In Konqueror z.B. brauchst du nur mit der Maus über den Dateinamen zeigen, und dann siehst du den Kommentar unter der Vorschau.

monta hat geschrieben:Da bin ich mal gespannt, wanns ne offizielle Version gibt.

Ich auch! ;-) Muss aber noch einiges verbessern. Nur dass es funktioniert, heisst noch nicht, dass die Struktur schon so ist, wie man's haben möchte.
Ausserdem wollte ich vor dem offiziellen "Release" noch eine Vorversion an alle schicken, deren Code ich verwendet habe. Ich habe schon eine offizilelle Zustimmung von Mike Lischke und John M. Miano. Ich werde gleich noch eine Anfrage an Eric Sibert schicken, dessen PNG-Write ich soeben eingebaut habe (Siehe Anhang).

monta hat geschrieben:zu P.S.2: ist mir noch nicht aufgefallen, und das problem hat ich trotz mehrfachem editieren auch noch nie, gibts nen genauen weg, das zu reproduzieren? (Eventuell per Pn)


Editieren -> Vorschau -> Editieren ->Vorschau: Irgendwann kommt das so (Mozilla).
Ist nicht schlimm.
Dateianhänge
logoop.png

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

Beitrag von theo »

Oder so: (gleicher Kontext wie oben)

Code: Alles auswählen

png:=TPNGImage.create;
png.Assign(jp);
png.PixelFormat:=pf32bit;
for i:=0 to (png.width * png.height) do TBitmapData32(png.Data).RawArray^[i].Alpha:=128;
png.SaveToFile('/home/theo/logoop.png');
png.free;


Den Alphakanal runterschrauben für das ganze Bild. Geht blitzschnell, da das RawArray über typecasting für den Programmierer zugänglich ist.
Wenn man's nicht "kompliziert" und nicht so schnell mag, kann man einfach nur z.B png.Pixels[x,y]:=AColor setzen.

Das wird dann automatisch im PNG gespeichert und sieht so aus:
http://www.theo.ch/lazarus/pngtst.htm
Der Hintergrund scheint teilweise durch.
Klappt vielleicht nicht mit Uralt-Browsern.

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

Beitrag von theo »

Ups, spät geworden.
Habe für interessierte hier noch eben eine neue Demo gebastelt:

Linux / GTK
http://www.theo.ch/lazarus/demo3.tar.gz

Damit kann man BMP, PNG, GIF und JPEG öffen und speichern.
D.h. umwandeln. Das ist schon fast ein bisschen brauchbar ;-)

Aber Achtung! Das ist nur so als Demo zusammengehackt! Also es wird eine bestehende
Datei gnadenlos überschrieben, wenn ihr den gleichen Dateinamen angebt.

Das Speichern erfolgt über die Dateinamenserweiterung. Also wenn ihr "test.png" im SaveDialog schreibt, gibt's ein PNG.
Im Moment noch mit den "defaults", also noch keine Dialoge für Qualität, etc.

Der "End-Programmierer" Quellcode ist denkbar simpel, hier seht ihr auch gleich was das Programm macht bzw nicht macht:

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
begin
  gp := TOPGraphic.create;
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  gp.free;
end;
 
procedure TForm1.OpenExecute(Sender: TObject);
begin
  OpenDialog1.Filter := OPGFilters;
  if OpenDialog1.Execute then
  begin
    try
      gp.LoadFromFile(OpenDialog1.FileName);
      AssignOpBitmapToBitmap(gp.Bitmap, Image1.Picture.Bitmap);
      Caption:=AppTitle+ExtractFileName(OpenDialog1.FileName);
    except
    end;
  end;
end;
 
procedure TForm1.SaveAsExecute(Sender: TObject);
begin
  SaveDialog1.Filter := OPGFilters;
  if SaveDialog1.Execute then
  begin
    try
      gp.SaveToFile(SaveDialog1.FileName);
    except
    end;
  end;
end;


Und wenn ihr euch nun fragt: "Was soll das, ausser GIF konnten wir ja alles auch so" dann bedenkt bitte, dass hier völlig unabhängige Codes eingesetzt werden.
So kann man damit z.B. wirklich (fast) alle BMP in allen BPP öffnen, Lazarus kann z.B. keine RLE-encoded Bitmaps öffnen
Daselbe mit JPEG: Lazarus kann keine Progressive JPEG öffnen.
Bei PNG weiss ich's nicht genau. Aber mit dieser Version ist vieles möglich, speichern von 16bit Grauformaten, Alphakanälen etc. Und GIF wisst ihr ja.
Und weitere Formate zum Öffnen werden noch folgen: Photo CD, PSP, PSD (kombiniertes Bild), Tiff, Targa etc.
Und alles ohne externe Libs und ohne Widgetset-Abhängigkeiten. Das Kann man also auch nur als FPC-Konsolenprogramm einsetzen und braucht keinen X-Server.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6209
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Beitrag von af0815 »

procedure TForm1.Button1Click(Sender: TObject);
var a,b:Byte;
begin
{.$Q+}
a:=91;
b:=84;
Edit1.text:=(intToStr(a + b - 256)); // Delphi gibt -81, FPC gibt 4294967215
end;

Um auf die Frage zurückzukommen:

256 ist doch nicht Byte, sonden Integer (Byte= ohne Vz. 0-255 bzw. mit Vz. +126 bis -127)

damit wird die Rechnung implzit auf integer erweitert, dort dürfte die Erweiterung scheinbar nicht konsistent durchgeführt worden sein.

Abhilfe: das ganze selbst richtig casten und nicht den Compiler raten lassen.
procedure TForm1.Button1Click(Sender: TObject);
var a,b:Byte;
begin
{.$Q+}
a:=91;
b:=84;
Edit1.text:=(intToStr(integer(a) + integer(b) - 256)); // Delphi gibt -81, FPC gibt -81
end;


das Beispiel geht (auch mit {$Q+}) ohne Probleme . Lazarus zeigt -81 an. Generell ist immer dann heikel wenn der Zahlraum innerhalb der Rechnung überschritten wird.

Nehmen wir an, du will nur 250 abziehen und bleibst dabei im Bytebreich.
procedure TForm1.Button1Click(Sender: TObject);
var a,b,c:Byte;
begin
{.$Q+}
a:=129;
b:= 140;
c := a + b - 250;
Edit1.text:= intToStr(c);
end;


Vielleicht kommt das richtige heraus, eigentlich solltest du mit einer RangeError Exception zur Laufzeit zurechtgestutzt werden, im schlimmsten Fall mit einem falschen Ergebnis, im besten Fall wird implizit auch Integer erweitert und dann auf Byte zurückgewandelt.
In jedem Fall ist es sehr heikel. Ich würde wenn möglich auf Bitmanipulation direkt umsteigen, da sind die Ergebnisse deterministischer :-) und vor allen die impliziten Umwandlungen auf explizit (definieren was DU willst) umstellen.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

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

Beitrag von theo »

af0815 hat geschrieben:In jedem Fall ist es sehr heikel. Ich würde wenn möglich auf Bitmanipulation direkt umsteigen, da sind die Ergebnisse deterministischer :-) und vor allen die impliziten Umwandlungen auf explizit (definieren was DU willst) umstellen.


Danke. Es funktioniert jetzt mit dem typecasting.
Ich war nur etwas erstaunt, dass es doch manchmal solche nicht unwesentlichen Unterschiede zwischen Delphi und FPC gibt.

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Das liegt daran das delphi oft versucht zu erraten was der entwickler meint sachlich richtig ists meisst so wie fpc es macht mit mode delphi funktioniert das mit dem raten zwar schon ganz gut nur eben nicht in alles fällen
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

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

Beitrag von theo »

Der Vollständigkeit halber:
Habe hier von Peter Vreman eine Antwort von auf die anfängliche Frage dieses Threads erhalten:
http://www.freepascal.org/mantis/view.php?id=8321

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

@theo

Zur Ausgangsfrage, Delphi benutzt für IntToStr den Typ Variant und rechnet die Parameter auf den höchstens zu erwartenden Typ um. Es kommt also entscheidend darauf an, welche Datentypen die Variablen haben. Manchmal klappt das aber selbst bei Delphi nicht richtig und du mußt einen Typecast machen:

Code: Alles auswählen

var
  a: int64;
  b: longint;
  v: string;
begin
  v:= IntToStr(longint(a) + b - longint(256));
end;


Nun weiß ich nicht sicher wie FPC das handhabt, es scheint aber auf das gleiche hinauszulaufen. Wahrscheinlich wählt hier FPC das kleineste mögliche Format für den konstanten Wert (256), der wäre aber Byte. Damit fällt er wegen der falschen Darstellung aus dem Rahmen. Der Typcast sollte das vehindern.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

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

Beitrag von theo »

@Schnullerbacke:
Die Frage ist ja nicht mehr, wie man's umgehen kann, das hatte ich ja schnell raus.
Ich finde es aber immer noch einigermassen beunruhigend, dass eine so "einfache Rechnung" wie "a + b - 256" (mit a und b als Bytes) zu so unterschiedlichen Ergebnissen zwischen D/K und FPC führt.
Zum Glück zeigt FPC wenigstens mit {$Q+} einen EIntOverflow, sonst würde man wohl beim Code-portieren das Problem eher später finden.

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

In dem Falle hätte ich dann mal die unit Variants im Verdacht. Bei unterschiedlichen Typen wird die Berechnung bei Delphi über die abgewickelt. Möglicherweise benutzt FPC die immer direkt, dann könnte sowas dabei rauskommen weil er eine Konstante nicht umwandeln kann. Das macht Delphi ohne Zwang durch den Typecast nämlich auch nicht.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

Antworten