Windows GDI und WinAPI

Antworten
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 »

Hmmm, na gut. Warum Array, nimm doch gleich TMemoryStream dann kann man gleich per TFileStream einlesen und von da aus in TMemoryStream auflösen.

Von mir aus auch direkt über Pascal-Files mit BufRead, BufWrite. Bei Size = 1 kann man dann die Größe der eizulesenden Daten variabel bestimmen, z.B. gleich ganze Line lesen, die Länge hat man dann ja schon. Dann wäre man völlig frei, das ist bei allen Dialekten gleich weil ISO. Z.B. den Mime-Header je nach Format in einer Rutsche.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

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

Beitrag von theo »

schnullerbacke hat geschrieben:Hmmm, na gut. Warum Array, nimm doch gleich TMemoryStream dann kann man gleich per TFileStream einlesen und von da aus in TMemoryStream auflösen.

Von mir aus auch direkt über Pascal-Files mit BufRead, BufWrite. Bei Size = 1 kann man dann die Größe der eizulesenden Daten variabel bestimmen, z.B. gleich ganze Line lesen, die Länge hat man dann ja schon. Dann wäre man völlig frei, das ist bei allen Dialekten gleich weil ISO. Z.B. den Mime-Header je nach Format in einer Rutsche.
Erstens muss ich auf die einzelnen Pixel zugreifen können. Das sind ja nicht nur Bytes sondern z.B. auch Records:

Pixel32 = packed record
Blue, Green, Red, Alpha: Byte;
end;

function TBitmapData32.GetNativePixel(X, Y: Integer): Pixel32;
begin
if not CheckPixelValid(X, Y) then exit;
Result := fPixels^[Y * fWidth + X];
end;

Das wäre ja ziemlich unpraktisch, mit unnötigem Overhead verbunden und auch nicht schneller mit TMemoryStream.

Zweitens handelt es sich hier ja um die interne Repräsentation.
So wird das sowieso nicht gespeichert, sondern über die Export / Import Algorithmen (JPEG, PNG, BMP...)

Drittens: Was das mit Mime-headern zu tun hat ist mir schleierhaft.

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 »

Soweit ich mich düster erinnern kann, darf man einen packed record (bei PASCAL ist eh alles packed was record heißt) auch mit move(source, dest, size) füllen. (packed stammt noch aus Wirth's Zeiten, das dient der Abwärtskompatibilität, hat also praktisch keine Auswirkung mehr. Es sei denn die sehen das bei FPC anders?)

Und dann sind ja nicht alle BMP = BMP, für die unterschiedlichen Revisionen gibt es jeweils einen Header der die Revision wiederspiegelt. Das gilt auch so für PNG und andere. Erst wenn du den hast, kannst du entscheiden wie weiter mit den Daten zu verfahren ist. Respektive kann man testen ob in dem File überhaupt die richtigen Daten angewackelt kommen. Kann schließlich einer das falsche Dateianhängsel drangeklebt haben.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

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

Beitrag von theo »

Schnullerbacke, du rauchst wohl wieder dein scharfes Zeug? ;-)

Du redest so einen Stuss daher und liest offenbar gar nicht was ich schreibe.

Melde dich wieder wenn du klar siehst, OK?

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 »

Pardon, nochmals ich bin Rauschmitteln grundsätzlich abhold. Aber über Dateiverarbeitung erzählst du mir nicht viel. Ich hab hier vor nicht allzu langer Zeit ne Datei gebastelt die verschiedene Daten (Text, sound, Grafik) mit varianter Recordgröße speichern kann und die man auch der Reihe nach wieder zurücklesen kann. Das alles in Form einer verketteten Liste. Die passenden Stream-Routinen gibt es auch dazu, die sollten sogar unter FPC problemlos laufen. Die popeln mir sogar noch ein bestimmtes Datenfeld aus dem Stream und extrahieren das, verkürzen also den Stream.

Wenn du also eine Grafik aus einer Datei auslesen willst mußt du erstmal wissen in welcher Struktur die Daten im File vorliegen. Schließlich ist ein File nix anderes als die Aufeinanderfolge von Bytes die so betrachtet alles mögliche sein können. Der Dateityp alleine gibt noch keinen Aufschluß über die Farbtiefe, die kann trotz gleichem Dateityp unterschiedlich sein. Darüber gibt der Header aber Aufschluß wie man das zu behandeln hat und Punkt.

Das führt dann zu folgender Überlegung, bau ich ein Objekt das alle Formate verarbeitet oder bau ich für jedes Format ein Objekt. Immerhin kann man die Struktur innerhalb der Datei auch mit einem Record beschreiben und dann so lustige Sachen wie:

Code: Alles auswählen

procedure TuWas(Canvas: Tcanvas; DataSize: Int64; var Buf);
type
 
  TGrafFormat = record
     width,
     height : integer;
     Palette: array of integer;
     PixMap: array of byte;
  end;
 
var
  width,
  heigth,
  PalMaxInd: integer;
 
begin
  width:= TGrafFormat(Buf).width;
  height:= TGrafFormat(Buf).height;
  PalMaxInd:= High(TGrafFormat(Buf).Palette) - 1;
  // und so weiter und so weiter...
end;
machen. Ich brauch also kein Array mit fester Größe und das schreiben auf das Canvas kann trotzdem passieren. Ist das abgearbeitet bleibt das Canvas übrig und gut dem Dinge. Das ist ja auch nix anderes als ein Pointer auf einen Speicherbereich. Das ich aber solche Records für fast alle Formate beschreiben kann dürfte auf der Hand liegen.

Und nun erzähl mir bloß nicht das Format im File sei schon deshalb klar weil da .bmp dran bummelt, das schränkt nur die Zahl der Möglichkeiten ein.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

ich würde einfach für jedes nur denkleche variation eines grafik formates einen eigenen header/oder eine eigene klasse zu schreiben nach diesem format hier:

Code: Alles auswählen

// von dieser klasse müssen alle grafik klassen abgeleitet sein
// sie würde die eigenschaften spiegeln von allen klassen
TGrafik = class
  procedure LoadFile(...)
  procedure SaveFile....
end
 
// achja die impaltion würde dann so aussehen:
loadfile(...)
// entscheide welches format du hast(erstmal nur grob, und später dann richtig)
// Wenn es ein bmp ist
TGrafik_BMP = class(TGrafik)
.....
problem dabei ist nur alle grafikformate(oder nur die bekanntesten:bmp,gif,png, jpeg,(evlt. auch viedo formate).... müssen eine gemeinsamme klasse haben.....

und die entscheidet dann nacheinem prüfen ob es ein grafikforamt ist wofür ich eine klasse habe oder nicht und ruft die dann auf !

oder müsen wir uns da an einer stadart klasse halten ?
ich währe dafür eine komplet neue klasse zu schreiben die von keiner abgeleitet ist.....

ich werde mal nach dateiformat beschreibungen suchen und mal mit BMP anfangen.....
MFG
Michael Springwald

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

Beitrag von theo »

pluto hat geschrieben: und die entscheidet dann nacheinem prüfen ob es ein grafikforamt ist wofür ich eine klasse habe oder nicht und ruft die dann auf !
Für das gibt's eigentlich TPicture und TGraphic

http://www.freepascal.org/docs-html/lcl ... ormat.html" onclick="window.open(this.href);return false;

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

Beitrag von theo »

schnullerbacke hat geschrieben:Pardon, nochmals ich bin Rauschmitteln grundsätzlich abhold.
Das Problem was ich mit dir habe ist, dass du einfach nie auf das Thema eingehst, sondern immer über irgendwas redest.

Wenn einer über Bratwürste spricht, dann redest du über Hausstaubmilben. ;-)
Wie wär's mal damit, das zu lesen was einer schreibt, und dann eventuell sogar nachzufragen? Im Stil: Theo, ich verstehe nicht ganz was dein Code macht, liest der nun Dateien ein? Ich verstehe aber nicht wie?
Das wäre doch mal ein Ansatz zur Kommunikation.

Ich habe ausführlich beschrieben worum es mir geht. Deine Antworten haben damit rein gar nichts zu tun. Ich sage ja nicht mal, das sei falsch was du schreibst, es ist bloss was ganz anderes.

Aber da dich mein Thema scheint's nicht interessiert, versuche ich wenigstens auf deins einzugehen:
Dir geht's also um die Erkennung von Dateitypen anhand ihrer "Header" sofern vorhanden. Dafür gibt's Listen à la: http://edin.dk/ftp/pub/php/win32/dev/te ... magic.mime" onclick="window.open(this.href);return false;
Für Farbtiefe etc brauchst du schon spezifischere Angaben, bzw den Reader des Dateiformats (BMP, PNG, JPEG, TIFF, GIF).

Und noch dazu: Mein Array hat keine feste Grösse. Wenn du dir den Code angeschaut hättest, wüsstest Du das. Hier nochmal das Beispiel:

Code: Alles auswählen

procedure TBitmapData16.UpdateSize;
begin
  if (fWidth > 0) and (fHeight > 0) then
  begin
    fLineLength := fWidth * 2;
    GetMem(fPixels, fHeight * fLineLength)
  end else
    if (fPixels <> nil) then begin
      FreeMem(fPixels);
      fPixels := nil;
    end;
end;

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

Beitrag von theo »

Ach und das @Schnullerbacke:

Ich weiss jetzt warum du gedacht hast das Array hätte eine fixe Grösse:

Das ist schon so definiert:

Code: Alles auswählen

APixel16 = array[0..MaxArr] of Pixel16;
  PAPixel16 = ^APixel16;
Allerdings gibt es keine Variable des Typs APixel16 im Code.
Nur einen Zeiger auf "sowas":

Code: Alles auswählen

TBitmapData16 = class(TBitmapData)
  private
    fPixels: PAPixel16;
In diesem Fall definiert APixel16 quasi die Maximalgrösse für PAPixel16.
Bis jetzt ist aber kein einziges Byte reserviert.
Das erledigt erst:

Code: Alles auswählen

GetMem(fPixels, fHeight * fLineLength)
Ich gebe zu, dass man das aus dem Zusammenhang gerissen leicht übersehen kann.

Und warum das? Nach allem was ich weiss, kann man so Geschwindigkeit, Ressourcenschonung und Komfort am besten unter einen Hut bringen.
Bin aber lernfähig ;-)

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

Beitrag von theo »

Also gut. Ich probier nochmal in einfachen Worten zu erklären was das ist, was ich da mache. Vielleicht interessiert's dann jemanden.

Ich schreibe ein völlig betriebssytemunabhäniges Bitmap (@Schnullerbacke BitmapWindows BMP Dateiformat). Es kompiliert mit Delphi/Kylix/FPC.
Es benötigt weder Winapi, noch X-Server (und auch kein GTK, QT....), lediglich RTL /FCL.

Das setzt natürlich voraus, dass alle gängigen Pixelformate von pf1bit bis pf64bit im Pascal-code dargestellt und ineinander umgewandelt werden können.
Nichts anderes zeigt das Beispiel auf der vorherigen Seite anhand des Pixelformats pf16bit.

Also: Das was normalerweise das Betriebssystem, X-Server oder GTK, Qt erledigt, findet hier im Pascal Code statt.
Auch Farbreduktion (von 64,48,32,24,16,15 Bit) auf Paletten (8,4,1 Bit) wird in diesem Code erledigt. Solchen Code gibt es nicht in der LCL/VCL/CLX.

Es ersetzt also gewissermassen für diesen Bereich das Betriebssystem und LCL/VCL/CLX.

Da die meisten Dateiformat-Schreib/Lese-Codes welche es in Pascal gibt für Delphi geschrieben sind, ist es sehr nah an TBitmap von Delphi, aber nicht von
diesem abgeleitet. Wenn es das wäre, wäre es automatisch nicht mehr unabhängig.
Aber es gibt z.B. Scanlines, Pixelformat etc.

Wozu das Ganze? Erstens ist es nun relativ einfach, vorhandene Delphi Schreib-Lese Code für BMP, JPEG, PNG, GIF, TIFF, Targa, Kodak Photo CD etc für dieses Bitmap zu portieren, da es ja fast wie ein Delphi Bitmap funktioniert.

Wenn diese Arbeit einmal getan ist, kann man TOPBitmap (so heisst es) für alle gängigen Pascal Compiler einsetzen. Es ist dann quasi ein (pascal-) universal TBitmap.
D.h. man kann den gleichen Pascal Code für die unterstützten Formate einsetzen für Lazarus, Kylix, Delphi, Mseide.., aber auch nur FPC für Server.. ohne externe Libraries zu verwenden!!!

Das ist doch nicht schlecht, oder?

@Schnullerbacke: Wenn du das jetzt immer noch nicht kapiert hast, und wieder etwas erzählst wie: "Manche Webserver geben aber den Mime Typ falsch an" dann...(setze hier deine übelste Phantasie ein ;-)

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 »

Wunnebar, deswegen die Idee mit BufRead(BufWrite). Das ist in Pascal-Dialekten gleich, weil es halt den normalen Dateizugriffen entspricht. Die Idee dahinter ist recht einfach. Kann ich ein Format lesen dann ich es genauso leicht auch wieder in die Datei schreiben. Das kommt dann der Konvertierung entgegen, insbesondere wenn man das von TObject ableitet was nun wieder ObjectPascal-Neutral sein sollte. Wenn nicht auch egal, dann baut man sich ein eigenes. In dem Fall könnte man einen Teil der Algorythmen schon in diesem Objekt abarbeiten und du müßtest das für TOPBitmap ziemlich "mundgerecht" vorliegen haben.

Das man das dann auch verhältnismäßig leicht um neue Formate erweitern kann ist klar. Nur für animierte Geschichten könnte das etwas unpraktikabel werden. Ob man die Daten dann in variable Arrays oder über Pointer speichert ist dabei ziemlich wurscht. Auf jeden Fall wird in beiden Fällen immer gerade soviel Speicher verbraucht wie unbedingt nötig ist. Nicht ein Byte mehr.

Also so eine Art Grafikformat-Container der auch gleichzeitig Rechenknecht ist.

Vielleicht haben wir uns jetzt besser verstanden.

Und dann mal dazu:

Code: Alles auswählen

APixel16 = array[0..MaxArr] of Pixel16;
  PAPixel16 = ^APixel16;
 
  TBitmapData16 = class(TBitmapData)
  private
    fPixels: PAPixel16;
 
begin
  GetMem(fPixels, fHeight * fLineLength)
MaxArr wird ja wohl einen festen Wert haben. Ob du mit GetMem(blabla) verhindern kannst, das er für das MaxArr + 1 den Speicher reserviert wage ich zu bezweifeln. Das würde auch den PASCAL-Regeln widersprechen. Du setzt damit nur das Ende des Pointers auf die gewünschte Adresse, der Speicher dürfte aber trotzdem verbraucht sein. Die Konstruktion macht auch in sich keinen Sinn. GetMem würde den Speicher auch dann richtig reservieren wenn du:

GetMem(fpixels, fHeight * fLineLength * SizeOf(Pixel16)) // Pixel16 = 2 byte vermute ich mal

angeben würdest. Dann dürfte aber fPixels nur als pointer deklariert sein. Das ist dann natürlich etwas unbequemer weil man nicht über das Array zugreifen kann. Aber ob Adress-Addition nun soviel schwerer als ein Array-Index ist weiß ich nicht so genau.

Naheliegender wäre da wohl:

Code: Alles auswählen

APixel16 = array of Pixel16;
//  PAPixel16 = ^APixel16;
 
  TBitmapData16 = class(TBitmapData)
  private
    fPixels: APixel16;
 
begin
  SetLength(fPixels, fHeight * fLineLength);
Dann haste dein Array genau in der passenden Größe und trotzdem ein Array. Nur Setlength(fPixels, 0) sollte man dann nicht vergessen sonst gibts Speicherleaks.

Ansonsten, "Freundschaft". Ich geh halt die Dinge gerne von vorne an, manchmal kommen dabei prima Ideen raus.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

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

Beitrag von theo »

schnullerbacke hat geschrieben:Wunnebar, deswegen die Idee mit BufRead(BufWrite). Das ist in Pascal-Dialekten gleich, weil es halt den normalen Dateizugriffen entspricht. Die Idee dahinter ist recht einfach. Kann ich ein Format lesen dann ich es genauso leicht auch wieder in die Datei schreiben.
Worauf du damit hinauswillst ist mir noch immer nicht klar.
Was hast du denn gegen SaveToFile oder SaveToStream?
Oder sprichst Du von Riesenbitmaps, wovon du nur einen Teil im Memory halten willst?
schnullerbacke hat geschrieben: Dann dürfte aber fPixels nur als pointer deklariert sein. Das ist dann natürlich etwas unbequemer weil man nicht über das Array zugreifen kann. Aber ob Adress-Addition nun soviel schwerer als ein Array-Index ist weiß ich nicht so genau.
Und was hab ich davon?
schnullerbacke hat geschrieben: Naheliegender wäre da wohl:

Code: Alles auswählen

APixel16 = array of Pixel16;
//  PAPixel16 = ^APixel16;
 
  TBitmapData16 = class(TBitmapData)
  private
    fPixels: APixel16;
 
begin
  SetLength(fPixels, fHeight * fLineLength);
Dann haste dein Array genau in der passenden Größe und trotzdem ein Array. Nur Setlength(fPixels, 0) sollte man dann nicht vergessen sonst gibts Speicherleaks.
Das wäre dann ein dynamisches Array. Das ist wieder leicht was anderes.
Setlength(fPixels, 0) braucht das nicht unbedingt. Funktioniert wie Lange Strings.
Wenn keine Referenz mehr auf die Variable besteht, wird der Speicher automatisch freigegeben.

Aber das ist doch nur Geplänkel hier mit den Arrays. Das ist doch nur ein winziges Detail, das man auch noch ändern kann.
schnullerbacke hat geschrieben: Ansonsten, "Freundschaft". Ich geh halt die Dinge gerne von vorne an, manchmal kommen dabei prima Ideen raus.
Ich streite ja nicht mit dir. Und die Dinge von vorne angehen ist ja genau was ich versuche. Aber irgendwie muss man doch auch mal auf dem Punkt bleiben, sonst kriegt man ja nie was gebacken.

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 »

BufRead und BufWrite sind beides IO-Prozeduren die nicht das Geringste mit Delphi oder irgendeiner anderen Implementierung von Object-Pascal zu tun haben. Da liegen nur noch die OS-Prozeduren drunter. Weiter weg vom Dialekt kann man also nicht sein.

Bei SaveToFile findet man im Delphi-Code den Aufruf von SaveToStream, die Dinger hab ich sofort im Verdacht bei anderen Implementierungen anders auszusehen. Durch X Hintertüren greifen die dann letztlich aber auch nur auf BufRead(BufWrite) zu. Im Sinne der Unabhängigkeit kann man die dann auch gleich benutzen. Das verschafft einem auch noch ein paar zusätzliche Freiheiten beim einlesen und schreiben der Daten.

Das ist dann eben PASCAL-Urschlamm, weil du ja möglichst bei beliebigen Implementierungen das gleich erreichen willst also naheliegend das zu nutzen. Null Overhead gibt es dabei frei Haus.

Also was schimpfst du immer mit mir?
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

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

Beitrag von theo »

schnullerbacke hat geschrieben:BufRead und BufWrite sind beides IO-Prozeduren die nicht das Geringste mit Delphi oder irgendeiner anderen Implementierung von Object-Pascal zu tun haben. Da liegen nur noch die OS-Prozeduren drunter. Weiter weg vom Dialekt kann man also nicht sein.
Ja, aber das ist doch hier mal wieder nicht das zentrale Problem.
Die Schreiberei und Leserei erledigen sowieso die Encoder und Decoder für die verschiedenen Formate. JPEG anders als BMP etc. Das ist nicht mein Bier.
Ausserdem sehe ich kein Problem mit der Verwendung von TStream. Das funzt jetzt wirklich überall.
schnullerbacke hat geschrieben: Also was schimpfst du immer mit mir?
Ich schimpfe nicht. Ich versuche manchmal nur die Diskussion von Nebengeleisen auf das Wesentliche zu bringen, was mir offenbar nicht immer gelingt. ;-)

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

aber musst du die "Encoder und Decoder" funktionen nicht auch selbst schreiben ?
oder greifst du dann auf vorhande codes zurück ?
MFG
Michael Springwald

Antworten