Entwickler-Ecke

Multimedia / Grafik - TBitMap transparent zeichnen - StrechDraw verzerrt


Sylvus - Mo 07.04.08 15:23
Titel: TBitMap transparent zeichnen - StrechDraw verzerrt
Hi Leute, also ich will folgendes erreichen. Auf einem Hintergrund ziehen Wolken entlang, durch die man den Hintergrund durchscheinen sieht. Also z.B. zu 50%. Folgende Probleme:

Die Wolken habe ich als TBitMap;
Die Wolken haben ein grünes Umfeld, das mit transparent=true; ausgeblendet wird;
Ich zeichne die Wolken in ein Puffer, indem ich zuerst ein Hintergrund reinzeichne und die Wolken dann darauf setzte.

So, dass sind so die Hauptprobleme, das ich noch nen Anfänger bin, und dass sich die Wolken bewegen, sollte klar sein.
Genug geredet, hier der Code:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
procedure TForm1.Timer1Timer(Sender: TObject);
var
  I: integer;
begin
    Wolke;                                     
    Puffer.Canvas.Draw(00, Background);
    for i := 0 to 3 do
    begin
      Puffer.Canvas.StretchDraw(rect(WolkenPos[I].X, WolkenPos[I].Y, WolkenPos[I].X + WolkenPos[I].X div 2 div 4 * 2, WolkenPos[I].Y + WolkenPos[I].X div 5 - 10), Wolken[i]);
    end;
    Image1.Canvas.Draw(00, Puffer);            //Pufferzeichnung
end;

procedure TForm1.Wolke;
var
  I, K, y, x, Anzahl: integer;
  P1, p2: PByteArray;
begin
  for I := 0 to 3 do
  begin
    if WolkenPos[I].X div 2 div 4 * 2 < 5 then //Wolkenbewegung
      WolkenPos[I].X := 1200;
    WolkenPos[I].X := WolkenPos[I].X - (Lvl.Tag + 3);
    Anzahl := 1;
    for K := 0 to Anzahl do
    begin
      for y := 0 to Wolken[I].height - 1 do
      begin
        P1 := Wolken[i].ScanLine[y];
        p2 := Background.ScanLine[Y + WolkenPos[i].Y];
        for x := 0 to 900 do
        begin
          P1[x] := p1[x] + round((p2[x] - p1[x]) / Anzahl * K);
        end;
      end;
      Wolken[i].canvas.refresh;
    end;
  end;
end;


Funktioniert, natürlich nicht. Also folgendes:
-Die Wolken sind zu durchsichtig
-Es ruckelt
-Ich kann meine schöne Wolkenform vergessen und hab nur noch ein Viereck.
-Der Hintergrund der Wolken, entspricht dem Hintergrund an einer falschen Stelle.
Sprich, durch die Wolken scheint der Hintergrund von einer falschen Stelle.

Ja kann daran liegen, dass ich mir aus vielen Foren was zusammen geklaut habe, aber könnt ihr mir trotzdem helfen?
Danke, Viele Grüße Sylvus

P.S. Wenn ich weiter bin, editiert ich hier rein!

//1.Edit
Ich hänge mal ein Screen an, damit ihr wisst, was ich meine!

//2.Edit
Ich überleg grad und glaube es liegt an folgender Zeile, dass die Bilder nicht aktualisiert werden:

Delphi-Quelltext
1:
Wolken[i].canvas.refresh;                    

Was muss ich da anders machen?

Moderiert von user profile iconChristian S.: Luckies Namen aus Titel entfernt


busybyte - Mo 07.04.08 16:57

Bei so aufwändigen Grafiken siehe OpenGL oder DirectX.
Bei mir sieht das so aus das mit OpenGL meine CPU-Auslastung (2*3GHz) bei ca 35% liegt aber meine Geforce2 die Bremse ist (2,5FPS) bei 1024*1024.Neue GK ist schon bestellt.
Ausserdem ist ein standard Timer absolut ungeeignet für Grafikrefresh's.
OpenGL ist dank DirectX10 nicht mehr aktuell.Allerdings ist OpenGL im Gegensatz zu DirectX Plattformunabhängig.


Horst_H - Mo 07.04.08 18:10

Hallo,

Du korrigierst beim Hintergrund die y Position, lässt aber das verschobene x ausser acht.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
procedure TForm1.Wolke; 
var 
  I, K, y, x, Anzahl: integer; 
  P1, p2: PByteArray; 
begin 
  for I := 0 to 3 do 
  begin 
    if WolkenPos[I].X div 2 div 4 * 2 < 5 then //Wolkenbewegung 
      WolkenPos[I].X := 1200
    WolkenPos[I].X := WolkenPos[I].X - (Lvl.Tag + 3); 
    Anzahl := 1
    for K := 0 to Anzahl do 
    begin 
      for y := 0 to Wolken[I].height - 1 do 
      begin 
        P1 := Wolken[i].ScanLine[y]; 
        p2 := Background.ScanLine[Y + WolkenPos[i].Y]; 
        WStart := WolkenPos[I].X;
        for x := 0 to 900-WStart do 
        begin 
//hier muesste auch X für Background korrigiert werden
//p2[x] sollte eher p2[X + WolkenPos[i].X] oder so sein, aber muss dann begrenzt werden
//sodass [X + WolkenPos[i].X]< 900 bleibt, apropos 900 Pixel sind von 0..899
//K ist 0..1 für K = 0 ist diese Berechnung p1= p1 also sinnlos
//Faktioren vorher zusammenfassen Faktor :=  1/ Anzahl * K und damit multiplizieren ist schneller.
          P1[x] := p1[x] + round((p2[x] - p1[x+WStart]) / Anzahl * K); 
        end
      end
      Wolken[i].canvas.refresh; 
    end
  end
end;
{
In FarbAnimation.zip in http://www.delphi-forum.de/viewtopic.php?t=66816 habe ich den Faktor durch eine Integerzahl ersetzt statt 1/Anzahl * K einfach zu IntFakt := round((1 shl 16)/Anzahl * K).
Dann  wird
}

 P1[x] := p1[x] + round((p2[x] - p1[x+WStart]) / Anzahl * K); 
//zu
 P1[x] := p1[x] + ((p2[x] - p1[xWStart]) *IntFakt) shr 16;

Zum Klicken... In FarbAnimation.zip in http://www.delphi-forum.de/viewtopic.php?t=66816
Gruß Horst


Sylvus - Di 08.04.08 21:53

hm irgendwie ist mir das alles zu komisch also folgender Code:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure TForm1.Wolke;
var
  I, y, x: integer;
  P1, p2: PByteArray;
begin
  for I := 0 to 3 do
  begin
    if WolkenPos[I].X div 2 div 4 * 2 < 5 then //Wolkenbewegung
      WolkenPos[I].X := 1200;
    WolkenPos[I].X := WolkenPos[I].X - (Lvl.Tag + 3);
    for y := 0 to Wolken[I].height - 1 do
    begin
      P1 := Wolken[i].ScanLine[y];
      p2 := Background.ScanLine[Y + WolkenPos[i].Y - 1];
      for x := 0 to 900  do
      begin
      P1[x] := round((p1[x]-p2[x+WolkenPos[I].X])/2);
      end;
    end;
   Puffer.Canvas.StretchDraw(rect(WolkenPos[I].X, WolkenPos[I].Y, WolkenPos[I].X + WolkenPos[I].X div 2 div 4 * 2, WolkenPos[I].Y + WolkenPos[I].X div 5 - 10), Wolken[i]);
  end;
end;


Also so zeigt er mir die Wolken ganz bunt an und eher so als ob mein Bildschirm einen Fehler hätte.
Nochmal: Ich scane die erste Reihe, gehe dann in jeden einzelnen Pixel rein, und veränder diesen.
Das mach ich mit allen Reihen und dann zeichne ich mir meine Wolke. Wo liegt das Problem?
Offensichtlich an:

Delphi-Quelltext
1:
      P1[x] := round((p1[x]-p2[x+WolkenPos[I].X])/2);                    

Mein Vorgänger sagte zwar schon sehr viel dazu, aber ich versteh nicht warum man da shr 16... benutzen muss. Warum geht das nicht so?
Die "2" ist natürlich austauschbar, aber die müsste eigentlich eine 50% Transparents erzeugen, oder?
P1[x] gibt doch einfach einen Wert von 0 bis 255 wieder...
Hm irgendwie verzweifele ich grad, aber sonst kann mir mein Info Lehrer morgen ja auch noch was dazu sagen... :)

Ok über Hilfe bin ich wie immer sehr dankbar!
Viele Grüße Elias


Horst_H - Mi 09.04.08 08:56

Hallo,

ich will Dich nicht verschrecken. Es ging nur darum, es schneller zu machen, da round einiges an Zeit braucht.
Schneller machen sollte man immer erst, wenn es auch funktioniert.

Nun zu den falschen Farben:
Schau einfach mal den Aufbau eines Bildpunktes im Hauptspeicher an.
Das richtet sich nach dem Bitmap.PixelFormat.
Deine Vorgehensweise funktiniert nur bei pf24bit (R,G,B jeweils in einem Byte) und pf32bit (R,G,B und Transparenz jeweils in einem Byte), da diese jeden Farbkanal in einem seperaten Byte speichern.

Das bedeutet, dass WolkenPos[I].X muss um den Faktor BytesPerPixel vergrößert werden.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
{Ich hoffe das trifft es
// meist steht 0 in Bmp.pixelfornat drin, auflösung des Monitor?????
const
  BytesPerPixel :array [pf1bit..pf32bit] of double = (0.125,0.5,1,2,2,3,4);
}

für pf32bit ist es BytesPerPixel = 4 und für 24 bit eben 3.


P1[x] := round((p1[x]-p2[x+WolkenPos[I].X*BytesPerPixel]/2);


Uups, jetzt wird noch klar was schief läuft, dein x läuftz nicht über 900 Pixel sondern nur über x div BytesPerPixel.
Das musst Du noch anpassen.
Viel Erfolg dabei.

Gruß Horst


Sylvus - Mi 09.04.08 12:55

Argh, also ich glaub langsam komm ich da hinter, hatte zwar heute nen 10 minütiges Gespräch mit meinem Info Lehrer, aber der hat auch keine Ahnung :D
Also folgendes, ich hab meine Bilder ja als 24bit Typen, d.h. die Pixel sind aus 3 Werten aufgebaut.
So meine Überlegung sah dann wie folgt aus:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
procedure TForm1.Wolke;
var
  I, y, x,k: integer;
  P1, p2: PByteArray;
begin
  for I := 0 to 3 do
  begin

    if WolkenPos[I].X div 2 div 4 * 2 < 5 then //Wolkenbewegung
      WolkenPos[I].X := 1200;
    WolkenPos[I].X := WolkenPos[I].X - (Lvl.Tag + 3);
    Wolken[I].PixelFormat := pf24Bit;
    for y := 0 to Wolken[I].height - 1 do
    begin
      P1 := Wolken[i].ScanLine[y];
      p2 := Background.ScanLine[Y + WolkenPos[i].Y - 1];
      for x := 0 to Wolken[i].width do
      begin
      for k:=1 to 3 do begin
        P1[x] := round((p1[x]-p2[(X+WolkenPos[I].X)*k])/2);
        end;
      end;
    end;
   Puffer.Canvas.StretchDraw(rect(WolkenPos[I].X, WolkenPos[I].Y, WolkenPos[I].X + WolkenPos[I].X div 2 div 4 * 2, WolkenPos[I].Y + WolkenPos[I].X div 5 - 10), Wolken[i]);
  end;
end;


Damit müsste ich doch eigentlich jeden Pixel ansprechen und ihn dann überschreiben. Wenn man weiter denkt müsste es auch eigentlich so heißen:

Delphi-Quelltext
1:
P1[x] := round((p1[x*k]-p2[(X+WolkenPos[I].X)*k])/2);                    


Aber leider zeigt es immer noch nicht so den Erfolg. Was habe ich falsch gemacht, bzw falsch verstanden?
Tut mir Leid, falls ich mich grad son bissle doof stelle, aber ich habs halt noch nie gemacht :)
Ich werd auch mal mein Projekt im Anhang dazu fügen, vielleicht hast du ja mal Zeit, es anzuschauen.
Viele Grüße Sylvus

//edit wenns nen E/A Error 103 beim ausführen gibt, einfach weiter laufen lassen, arbeite grad dran, liegt daran, dass er irgendwie die Highscore.txt nicht immer öffnen kann. :D


Lossy eX - Mi 09.04.08 14:35

Wie wäre es, wenn du zur Einfachheit lieber einen Typen wie PRGBTriple benutzt? Denn das ist ein 3 Byte großer Datentyp mit dem du direkt auf die Komponenten eines Pixels zugreifen kannst. Außerdem ist es verständlicher (du weißt eher wo du bist) und du hast die innere Schleife nicht. Bei 32 Bit Bildern kannst du als Datentyp auch ein PRGBQuad verwenden.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var
  BMP: TBitmap;
  X, Y: Integer;
  pPix: PRGBTriple;
begin
  for Y := 0 to BMP.Width -1 do begin
    // erstes Pixel
    pPix := BMP.ScanLine[Y];

    for Y := 0 to BMP.Width -1 do begin
      // pixel verrechnen
      pPix^.rgbtRed   := pPix^.rgbtRed   + 2;
      pPix^.rgbtGreen := pPix^.rgbtGreen + 2;
      pPix^.rgbtBlue  := pPix^.rgbtBlue  + 2;

      // nächstes pixel
      Inc(pPix);
    end;
  end;
end;


Die innerste Schleife: Die muss in jedem Fall raus!! Außerdem darfst du dort auf gar keinen Fall irgendwelche Positionen berechen. Diese Stelle ist das letzte in der Schleifenhirarchie und wird entsprechend am Häufigsten aufgerufen. Bei Einem 512x512 großen Bild schlappe 786.432 Mal. Du solltest alle Positionen bereits so berechnen, dass du innerhalb von "for Y := 0 to BMP.Width -1 do" nur noch die Pixel verrechnen musst und die Positionen auf das nächste Pixel setzen musst. Alles andere kostet unnötig Zeit und multipliziert sich entsprechen auf.

Zum Berechnen der Pixel solltest du nach Möglichkeit kein Round/Trunc (Fließkomma operationen) oder DIV verwenden. Div ist mitunter noch schlimmer als Round. Ich muss gestehen, dass ich nicht genau weiß was du dort berechnest aber zum Teilen eines Wertes mit 2 kannst du auch "shr 1" machen. Dabei wird der Wert um ein Bit nach rechts verschoben was einem "/ 2" entspricht nur, dass es direkt auf der binären Datenebene stattfindet und nicht berechnet werden muss.

Zu guter Letzt: StretchDraw ist auch mit Vorsicht zu genießen. Denn die GDI kann von Hardware beschleunigt werden. Kann. Das muss sie aber nicht. Wenn du jetzt an ein System gerätst an der die Grafikkarte diese Operation nicht beschleunigt muss das umständlich auf der CPU ausgeführt werden. Wenn du die Größe nicht veränderst dann solltest du es besser direkt mit BitBlt zeichnen. Selbst wenn das dann nicht beschleunigt wird sollte das nur aus einem simplen Speicherkopieren bestehen was kaum Risiken darstellen sollte. Falls du es doch in der Größe veränderst solltest du dir überlegen ob es nicht evtl. Sinn macht da mehrere Wolken Bilder von vorherrein zu hinterlegen.

PS: Je nach Aufbau deines Programmes wäre es evtl sogar auch praktisch, wenn du zu Beginn eines Bildes erst den Hintergrund in den Puffer zeichnest und dann die Wolken direkt in den Puffer verrechnest. Denn so könntest du dir eine ganze Zeichenaktion sparen. Aber das kommt auf den Aufbau drauf an.


Sylvus - Mi 09.04.08 15:55

Na das wird doch langsam. :D
Also ich versuch erstmal, zu verstehen und es so hinzu bekommen, dass es funktioniert. Danach versuch ich das zu optimieren :) Ja ich weiß, etwas umständlich aber sonst ist mir das zu viel auf einmal.
Also momentan habe ich folgenden Code:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
procedure TForm1.Wolke;
var
  I, y, x: integer;
  pPix1, pPix2: PRGBTriple;
begin
  for I := 0 to 3 do
  begin
    if WolkenPos[I].X div 2 div 4 * 2 < 5 then //Wolkenbewegung
      WolkenPos[I].X := 1200;
    WolkenPos[I].X := WolkenPos[I].X - (Lvl.Tag + 3);
    Wolken[I].PixelFormat := pf24Bit;
    for y := 0 to Wolken[I].height - 1 do
    begin
      pPix1 := Wolken[i].ScanLine[y];
       pPix2 := Background.ScanLine[Y + WolkenPos[i].Y - 1];
      for x := 0 to Wolken[i].width do
      begin
      //ShowMessage(inttostr(pPix1^.rgbtGreen));
       if (pPix1^.rgbtGreen=255AND (pPix1^.rgbtblue=0AND (pPix1^.rgbtred=0then
       pPix1:=pPix1
       else
       begin
        pPix1^.rgbtRed := (pPix2^.rgbtRed + 255DIV 2;
        pPix1^.rgbtGreen := (pPix2^.rgbtGreen + 255DIV 2;
        pPix1^.rgbtBlue := (pPix2^.rgbtBlue + 255DIV 2;
        end;
        Inc(pPix1);
        Inc(pPix2);
      end;
    end;
    Puffer.Canvas.StretchDraw(rect(WolkenPos[I].X, WolkenPos[I].Y, WolkenPos[I].X + WolkenPos[I].X div 2 div 4 * 2, WolkenPos[I].Y + WolkenPos[I].X div 5 - 10), Wolken[i]);
  end;
end;


Und er funktioniert!
So jetzt das große Problem:
Meine Wolken sind zwar transparent, jedoch zeigen sie immer den Hintergrund an einer bestimmten Stelle an.
Ich möchte aber, dass der Hintergrund durchscheint, bei dem sie auch grade sind. Sprich ich müsste zu dem pPix2 irgendwie meine WolkenPos[I].X dazu addieren.
Wie erreiche ich das? Über pPix2:=pPix2+WolkenPos[i].X; geht es nicht!
Und die andere Frage, was bedeutet dieses ^??

Auf jeden Fall schon mal ein großes Danke! Und wenn es funktioniert schau ich mal, wie ich das DIV 2... raus bringe :)
Viele Grüße Sylvus


Lossy eX - Do 10.04.08 08:53

Das Dach ^ bedeutet, dass die Variable davor ein Pointer ist und der Wert dereferenziert werden soll. Also, dass du auf die eigentlichen Daten zugreifen möchtest und nicht auf die Adresse innerhalb des Pointers. Einfaches Beispiel.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  By: PByte;

// erhöht die Adresse um 1 * die Größe von sich selbst. In diesem Falle
// ein Byte. Bei Word wären es 2 Byte usw...
Inc(By);

// Erhöht den Wert an der Stelle auf der der Pointer zeigt.
Inc(By^);


Erst verstehen und dann optimieren ist evtl. umständlich aber in jedem Fall der richtige Weg. Denn du kannst nichts optimieren wenn du nicht weißt wie es funktioniert. Außerdem. Um so komplizierter der Code dadurch wird um so schwieriger kann es werden ihn zum Laufen zu bewegen. Wenn du da schon mal einen hast der geht ist das in jedem Falle besser.

Zu den Problem mit den Wolken. Beim Zugriff auf Scanline deines Hintergrundes berücksichtigst du bereits die Y Position deiner Wolken. Der Pointer befindet sich aber immer auf dem ersten Pixel der Zeile. Nach Scanline musst du dann natürlich noch auf das richtige Pixel in der Horizontalen positionieren.


Delphi-Quelltext
1:
2:
3:
4:
      pPix1 := Wolken[i].ScanLine[y];
      pPix2 := Background.ScanLine[Y + WolkenPos[i].Y - 1];

      Inc(pPix2, WolkenPos[i].X); // Horizontale Position setzen


Sylvus - Do 10.04.08 18:42

hey super, find ich toll, dass es noch viele Leute gibt, die etwas mehr Ahnung als mein Lehrer und ich haben :D
Aber irgendwie ist es immer noch so um nen paar Pixel verschoben, naja mal schauen.
Auf jeden Fall ist das Ergebnis schon akzeptabel :D
DANKE Viele viele Grüße Elias


Lossy eX - Do 10.04.08 21:28

Ich mache aber auch seit Jahren hauptsächlich nur solche Dinge. ;)

Im Zweifel kannst du auch mal ein Bild davon anhängen. Ich denke da kann man dann sicher erkennen wo das Problem liegt. Und dir dann tipps geben wieso es verschoben aussieht.


Sylvus - Do 10.04.08 22:48

na das tue ich doch gerne :)
Sonst schreibe ich da jetzt einfach so was wie +10 oder so hin. Aber trotzdem mal das Bild. Wir wollen ja schließlich das Problem auf die feine Art lösen :D
Viele Grüße Sylvus


Lossy eX - Fr 11.04.08 10:19

Also laut dem Bild würde ich nicht sagen, dass es verschoben ist. Denn die rechte Seite der Wolke sieht wirklich verschoben aus. Aber was ist mit der linken Seite? Diese kleine Ecke der Wolke (links mittlere höhe) wird richtig fortgesetzt und es schaut an dieser Ecke so aus wie es sein soll. Also denke ich liegt dein Problem in der Art wie du die Wolke zeichnest. Nämlich mit StrechDraw und dann auch nicht in der originalen Größe. Dadurch zeichnest du den verrechneten Hintergrund kleiner ins Bild als er eigentlich ist.

Aktuell hast du auch das Problem, dass du die Wolken immer in voller Größe berechnen musst auch wenn du sie später gerade mal 30x30 Pixel groß zeichnest. Da geht auf jeden Fall einiges an Zeit verlohren.

Als Lösung würde ich da evtl. vorschlagen, dass du die Wolken vorberechnest. Also entweder unterschiedlich große Wolkenbilder oder beim Laden der Wolkenbilder diese eben mit Strechdraw auf ein anderes Bild zeichnen um so die größen zu verändern. Beim Zeichnen dann normal die Daten verrechnen und mit Draw (nicht StrechtDraw) die Bilder an einer entsprechenden Position zeichnen. Dann sollte es auch mit dem Hintergrund klappen.

Wobei du da evtl auch gleich die Wolken in den Hintergrund rechnen kannst und nicht erst temporär in ein Bitmap. Denn so würden auch Wolkenüberlagerungen gleich mit in die Berechnung einfließen. Weniger sichtbarkeit. Was derzeit schlicht ignoriert werden würde.


Sylvus - Sa 12.04.08 13:20

Hi,
ja, also ich versteh zumindest schon mal was mein Problem ist :)
Nur habe ich die Lösungsansätze nicht richtig verstanden. Also das Bild mit dem Hintergrund verrechnen, ok und wie?
Also irgendein Befehl, nach dem ich suchen kann, oder ein anderer Ansatzpunkt. Hab irgendwie keinen Plan wonach ich suchen muss :) Oder geh ich da ganz falsch ran?
Nen Tipp wäre super :D
Viele Grüße Sylvus


Delete - Sa 12.04.08 15:57

Was hat denn bitte sehr der Threadtitel mit dem Problem zu tun? Ich dachte hier ginge es um Vista oder so. :roll:


Sylvus - Sa 12.04.08 21:26

Hier ist zwar schon viel passiert, aber eigentlich sollte er darauf hinweißen, dass meine Transparenz nicht geht.
Der erste Teil des Titles kommt daher, dass man das ganze auch mit der WindowsFunktion AlphaBlending machen könnte, es da aber total langsam ist. :D Alles klar?
Sonst frag einfach, aber besser per PN sonst wird das hier nur Offtopic :) Viele Grüße Sylvus


Delete - Sa 12.04.08 22:19

Ich wollte nur darauf hinweisen, dass es sinnvoller ist einen aussagekräftigen Titel zu nehmen anstatt einen beid em amn sich den Sinn erst zusammen reinem kann, wenn man das Problem kennt.


Lossy eX - Mo 14.04.08 10:34

Sylvus: Was verstehst du denn genau nicht? Du darst das StretchDraw nicht so benutzen wie du es derzeit benutzt. Denn Aktuell benutzt du es um unterschiedlich große Wolken zu erzeugen. Und dadurch, dass du vorher den Hintergrund einrechnest wird der natürlich kleiner dargestellt. Was aber falsch ist. Und sogar ein bisschen überflüssig. ;)

Das was ich aber empfehlen würde ist folgendes.
Start der Anwendung:
- Laden / Erstellen von Wolken in verschiedenen Größen

Zeichnen:
- Hintergrund in den Buffer zeichnen
- Verschieden große Wolken mit Scanlines auf den Hintergrund aufbringen und dabei die Transparenz mit einbringen
- Puffer auf dem Bildschirm darstellen

Das wars eigentlich schon. Und so gesehen machst du das alles irgendwo schon (+ ein bisschen mehr). Du musst genau genommen nur die Reihenfolge und die verwendeten Bitmaps umstellen.


Sylvus - Mi 16.04.08 14:59

Ja, das hab ich verstanden, aber wie erstelle ich die Bilder in den unterschiedlichen Größen?
Also ich wollte eigentlich nicht 1000 verschiedene Bilder in Paint zeichnen und diese alle in mein Programm laden. Oder ist genau dies der Weg?
Ich hab halt im Moment keine Ahnung wie ich das StretchDraw umgehen kann.
Wenn mir da jemand etwas helfen könnte, wäre das toll.
Also z.B.
"Hier benutz den Befehl in der Schleife und du lädst dir damit automatisch alle Bilder, in verschiedenen Größen, in den Speicher!"

In guter Hoffnung Sylvus :D


Lossy eX - Do 17.04.08 11:25

Also 1000 Bilder von Vornherein zu erstellen dürfte sicher kompliziert werden. Würde dir aber empfehlen doch ein paar unterschiedliche zu erstellen. Das wirkt besser als wie wenn sie alle gleich aussehen.

Und je nachdem wie viele Wolken du gleichzeitig darstellen willst/musst gibt es 2 Möglichkeiten. Da aber bitte keine hypothetischen Zahlen wie 1000. Das wird dann vermutlich auch einfach nicht mehr aussehen.

Möglichkeit 1: Du erstellst beim Start die von dir benötigten Wolken. Und zwar jede in einem eigenen Bitmap. Welche die nahezugleich sind kannst du als eines betrachten. Das fällt nicht auf. Dazu errechnest du die Größe der Zielwolke, erstellst ein passenden Bitmap und zeichnest die originale Wolke mit StrechDraw in die Zielwolke. Die Zielwolke benutzt du dann später zum Zeichnen. Fertig.
Du musst die Wolken dann nur noch zeichnen. Verbrauchst aber je nach Anzahl der Wolken eine gehörige Mehrmenge an Speicher.

Möglichkeit 2: Du erstellst dir direkt vor dem Zeichnen ein kleinere Varainte der Wolke und benutzt dieses zum Verrechnen in deinen Hintergrund. Dadurch verbrauchst du nicht so viel Speicher aber der Aufwand zum kleiner Rechnen der Wolken kann durchaus um einiges höher sein. Es wird also schlicht langsamer.

Und StrechDraw: Wenn du deinen Ablauf geschickt aufbaust brauchst du eigentlich gar kein StrechDraw. Bis auf evtl beim Start (Möglichkeit 1). Aber sonst nicht. Das hatte ich ein Post weiter oben schon mal beschrieben. Falls du damit Verständnissprobleme hast tu dir keinen Zwang an und frage. Dann Erkläre ich das noch mal ausführlicher. Ich weiß nämlich nicht wie groß deine Fähigkeiten sind. ;)


Sylvus - Do 17.04.08 19:44

Argh, naja so ganz hab ich es nicht nicht :)
Also bei meinem Programm ziehen ja die Wolken von links nach rechts und werden dabei immer kleiner.
Damit dies nicht so ruckartig passiert, bräuchte ich schon so 100 Bilder, oder?
Deine Lösung habe ich wie folgt verstanden:
-Je nach Position der Wolke:
-In eine temporäre Variable das Bild mit strechdraw zeichnen
-Meine richtige Wolke mit der temporären überschreiben
-Richtige Wolke anzeigen und die Variable wieder freigeben

Richtig so? Also ich probiere es gerade mal so, wie oben beschrieben. Über eine Rückmeldung würde ich mich freuen :D
Viele Grüße Sylvus

//edit
hab grad mal was gefunde, was mich von StrechDraw befreien könnte. StretchBlt!
naja aber ich habs noch nicht ganz verstanden, hier mal der erste Versuch eines Codes (ja sehr falsch).
Bitte mich nicht gleich auslachen :D

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
     SetStretchBltMode(Puffer.Canvas.Handle, HALFTONE);
     StretchBlt(Puffer.Canvas.Handle,
     WolkenPos[I].X,
     WolkenPos[I].Y,
     WolkenPos[I].X + WolkenPos[I].X div 2 div 4 * 2,
     WolkenPos[I].Y + WolkenPos[I].X div 5 - 10,
     Wolken[I].Canvas.Handle,
     WolkenPos[I].X,
     WolkenPos[I].Y,
     WolkenPos[I].X + WolkenPos[I].X div 2 div 4 * 2,
     WolkenPos[I].Y + WolkenPos[I].X div 5 - 10,
     SRCINVERT);

Auf das es helfen möge.. :) LG Sylvus


Lossy eX - Fr 18.04.08 10:36

Schlechte Nachricht für dich. StrechDraw und StrechBlt ist ziemlich genau das Gleiche. Nur, dass eines innerhalb des TBitmaps gekappselt ist.

Ich muss gestehen, dass ich deine Herangehensweise nicht ganz verstehe. Das du die originale Wolke irgendwie überschreibst verwirrt mich etwas. Mag aber evtl auch daran liegen, dass ich heute noch nicht ganz auf der Höhe bin.

Okay. Ich wusste und dachte nicht, dass die Wolken von links nach recht kleiner werden. Hätte eher erwartet dass die unterschiedlich groß sind und nur von links nach rechts ziehen. Dann fällt natürlich die erste Methode raus. Du kannst nicht für jede Größe ein Wolkenbitmap erstellen. Der Speicherverbrauch wäre doch etwas zu viel des Guten.

Also bleibt wohl eher Methode 2. Und ich denke ein bisschen Code veranschaulicht das etwas besser wie ich denke. Dabei wird eine kleinere Kopie der originalen Wolke erstellt und diese in den Buffer eingerechnet.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
procedure TForm1.FormPaint(Sender: TObject);
var
  Buffer: TBitmap;
  Background: TBitmap;
  Cloud: Integer;
  OriginalCloud: TBitmap;
  TempCloud: TBitmap;
begin
  // Hintergrund in Buffer zeichnen
  Buffer.Canvas.Draw(00, Background);
  // oder
  Buffer.Assign(Background); // vermutlich besser, da nur kopiert wird

  // Wolken in Buffer zeichnen
  for Cloud := 0 to MaxClouds -1 do begin
    // Wolke erstellen und zeichnen
    TempCloud := TBitmap.Create;
    try
      // Größe festlegen
      TempCloud.PixelFormat := pf24bit;
      TempCloud.Width := 000;
      TempCloud.Height := 000;

      // Original Wolke in Temporäre zeichnen
      TempCloud.Canvas.StretchDraw (TempCloud.Canvas.ClipRect, OriginalCloud);

      // temporäre Wolke mittels Scanline auf Hintergrund aufbringen.
      // Das überlasse ich dir.
    finally
      TempCloud.Free;
    end;
  end;

  // Buffer zeichnen
  Form1.Canvas.Draw(00, Buffer);
end;


Sylvus - Sa 19.04.08 01:08

Ich bin wohl zu doof :D Vielleicht nen Link zu einem Delphi-Tutorial, welches mir nicht mehr erklärt was ne for-Schleife ist, aber mich mal über Scanline und Klasse... aufklärt? *g*
Also hier mein Code, wie er meiner Logik nach funktionieren müsste:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
    for I := 0 to 3 do begin
    // Wolke erstellen und zeichnen
      WolkenTemp[I] := TBitMap.Create;
      try
      // Größe festlegen
        WolkenTemp[I].PixelFormat := pf24bit;
        WolkenTemp[I].Height := WolkenPos[I].X + WolkenPos[I].X div 2 div 4 * 2;
        WolkenTemp[I].Width := WolkenPos[I].Y + WolkenPos[I].X div 5 - 10;
      // Original Wolke in Temporäre zeichnen
        WolkenTemp[I].Canvas.StretchDraw(WolkenTemp[I].Canvas.ClipRect, Wolken[I]);
      // temporäre Wolke mittels Scanline auf Hintergrund aufbringen.
  for y := 0 to WolkenTemp[I].height - 1 do
    begin
      pPix2 := WolkenTemp[I].ScanLine[y];
      pPix1 := Background.ScanLine[Y + WolkenPos[I].Y];
      for x := 0 to WolkenTemp[I].width - 1 do
      begin
          pPix1^.rgbtRed := pPix2^.rgbtRed;
          pPix1^.rgbtGreen := pPix2^.rgbtGreen;
          pPix1^.rgbtBlue := pPix2^.rgbtBlue;
        Inc(pPix1);
        Inc(pPix2);
      end;
    end;
      finally
        WolkenTemp[I].Free;
      end;
    end;
    Image1.Canvas.Draw(00, Puffer); //Pufferzeichnung


Funktioniert aber leider nicht, da jedes mal wenn ich die Funktion aufrufe Delphi abstürzt, mit der Fehlermeldung:
"Im Project Wolkenkatz.exe ist eine Exception der Klasse EInvalidGraphicOperation mit der Meldung "Bereichsüberschreitung bei Zeilenindex" aufgetreten."
Tja und jetzt?
Irgendwie bin ich echt nicht fähig :) Also über eine weitere Antwort würde ich mich sehr freuen!
Viele Grüße Sylvus

P.S. Ich hatte es auch schon mal hin bekommen, dass er alles mit einer riesigen Wolke übermalt, aber das kann es ja auch nicht sein^^


Lossy eX - Sa 19.04.08 09:22

Also deine Einrückung ist gruselig. ;)

Und ScanLines. Es gibt ein Tutorial aber den Link hab ich gerade nicht zur Hand. Aber Scanline macht nichts anderes als dir den Pointer auf den Datenbereich einer Bildzeile eines Bitamps zurückliefern. Mit anderen Worten. ScanLine[0] gibt den Pointer auf die erste Zeile zurück. Sobald du etwas dort hin schreibst wohin der Pointer zeigt landet es auch sogleich im Bitmap.

"Bereichsüberschreitung bei Zeilenindex" bedeutet du hast versucht auf eine nicht existierende Zeile zuzugreifen. Also Entweder kleiner "-1" oder größer "Height -1". Ich denke es liegt an dem Hintergrund. Du solltest natürlich vor den Zeichnen einer Zeile überprüfen ob der Hintergrund auch großgenug ist. Denn Hintergrund = 800 pixel hoch und du zeichnest eine 200 Pixel hohe Wolke an die Position 700, dann knallt es irgendwann.

Aber ich frage mich gerade warum du eine Array von TempWolken hast? Du hast nur eine Temporäre Wolke. Versuch es doch einfach mal so zu machen, dass du NUR die erste Wolke zeichnest. Und anschließend kannst du es erweitern, dass mehrere Wolken gehen. Denn wie du selbst festgestellt hast ist das kein leichtes Thema und da musst du dich vorsichtig ran tasten.


Sylvus - Sa 19.04.08 22:18

Warum eigentlich nicht so??:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
    Wolke;
    // Wolken in Buffer zeichnen
    for I := 0 to 3 do begin
      // Wolke erstellen und zeichnen
      WolkenTemp := TBitMap.Create;
      WolkenTemp.Transparent := True; //Problem
      try
        // Größe festlegen
        WolkenTemp.PixelFormat := pf24bit;
        WolkenTemp.Width := WolkenPos[I].X div 2 div 4 * 2;
        WolkenTemp.Height := WolkenPos[I].X div 5 - 10;
        // Original Wolke in Temporäre zeichnen
        WolkenTemp.Canvas.StretchDraw(WolkenTemp.Canvas.ClipRect, Wolken[I]);
        Wolken[I].Assign(WolkenTemp);
        Puffer.Canvas.Draw(WolkenPos[I].X, WolkenPos[I].Y, Wolken[I]);
      finally
        WolkenTemp.Free;
      end;
    end;
    Image1.Canvas.Draw(00, Puffer); //Pufferzeichnung


Dabei hab ich nur ein Problem, die Transparents :)
Also beim ersten mal zeichnem gehts noch super und dann wirds immer schlechter bis die Wolken gar nicht mehr neu auftauchen...also irgendwie geht das nicht so richtig.
Weiß auch nicht genau woran es liegt, könnte auch die Temporäre Wolke ins Puffer zeichnen also:

Delphi-Quelltext
1:
        Puffer.Canvas.Draw(WolkenPos[I].X, WolkenPos[I].Y, WolkenTemp);                    

aber dann funktioniert meine transparents nur bei ganz wenig Pixeln....hmmm irgendwie muss ich da was ändern, nur was :P Nochmals viele liebe Grüße Sylvus


Lossy eX - So 20.04.08 13:15

Die "Transparenz" die Delphi da meint ist eher ein Masking. Dabei wird eine transparente Farbe einfach ausgelassen. Der Rest wird voll gezeichnet. Und du brauchst aber richtige Transparenz. Es gibt zwar eine Funktion AlphaBlend in der Windows API aber damit habe ich bisher nichts gemacht. Die zweite Möglichkeit wäre das alles mit ScanLines zu lösen. Also händisch.


Sylvus - So 20.04.08 17:21

Hey, also naja am Anfang funktioniert es, dann aber nicht mehr...irgendwie liegt das noch an meiner Struktur.
Folgender Quellcode:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
//...
      Puffer.Assign(Background);
      Puffer.Canvas.StretchDraw(rect(SchornsteinPos.X, SchornsteinPos.Y, SchornsteinPos.X + 130, Image1.Top + Image1.Height), Schornstein);
      Puffer.Canvas.StretchDraw(rect(PlayerPos.X, PlayerPos.Y, PlayerPos.X + 130, PlayerPos.Y + 79), Player);
      if DachPos.X > -100 then
         Puffer.Canvas.StretchDraw(rect(DachPos.X, DachPos.Y, DachPos.X + 100, Image1.Top + Image1.Height), Dach);
      Wolke;
      Image1.Canvas.Draw(00, Puffer); //Pufferzeichnung

//...
procedure TForm1.Wolke;
var
   I, y, x: integer;
   pPix1, pPix2: PRGBTriple;
begin
   randomize;
   for I := 0 to 3 do
   begin
      if WolkenPos[I].X div 2 div 4 * 2 < 5 then //Wolkenbewegung
         WolkenPos[I].X := 1200;
      for y := 0 to Wolken[I].height - 1 do
      begin
         pPix1 := Wolken[i].ScanLine[y];
         pPix2 := Background.ScanLine[Y + WolkenPos[I].Y];
         Inc(pPix2, WolkenPos[I].X);
         for x := 0 to Wolken[I].width - 1 do
         begin
            if (pPix1^.rgbtGreen = 255and (pPix1^.rgbtblue = 0and (pPix1^.rgbtred = 0then
               pPix1 := pPix1
            else
            begin
               pPix1^.rgbtRed := (pPix2^.rgbtRed + WolkenColor[I] + 255div 3;
               pPix1^.rgbtGreen := (pPix2^.rgbtGreen + WolkenColor[I] + 255div 3;
               pPix1^.rgbtBlue := (pPix2^.rgbtBlue + WolkenColor[I] + 255div 3;
            end;
            Inc(pPix1);
            Inc(pPix2);
         end;
      end;
      WolkenTemp := TBitMap.Create;
      WolkenTemp.PixelFormat := pf24bit;
      Wolken[I].PixelFormat := pf24Bit;
      WolkenTemp.Width := WolkenPos[I].X div 2 div 4 * 2// Größe festlegen
      WolkenTemp.Height := WolkenPos[I].X div 5 - 10;
      WolkenPos[I].X := WolkenPos[I].X - (Lvl.Tag + 3) - Speedfactor;
      WolkenTemp.Canvas.StretchDraw(WolkenTemp.Canvas.ClipRect, Wolken[I]); // Original Wolke in Temporäre zeichnen
      Wolken[I].Assign(WolkenTemp);
      WolkenTemp.Transparent := true;
      Puffer.Canvas.Draw(WolkenPos[I].X, WolkenPos[I].Y, WolkenTemp);
      WolkenTemp.Free;
   end;
end;

Und das Programm findest du im Anhang :)
Na vielleicht hast du ja Lust mal rein zu schauen, ich probiere auf jeden Fall mal weiter :D
Viele Grüße Sylvus


Lossy eX - Di 22.04.08 21:07

Also ich muss gestehen, dass ich mit der Anwendung nichts anfangen kann, da es bei mir sehr schnell läuft. Evtl wäre es ganz praktisch, wenn du mal den den kompletten code inklusive alle benötigten Dateien hochlädst. So das wir uns den evtl mal anschauen. Denn mit dem Code weiß ich auch gerade nicht wo was faul sein könnte.


Sylvus - Mi 23.04.08 10:14

Wenns zu schnell ist, resete doch einfach mal das lvl :D

Ja, ich lad mal das komplette Projekt hoch, ist nicht kompiliert und die temporären Datein sind gelöscht (wegen der Größe). Hoffe du kannst es öffnen, habe es mit Delphi 2005 erstellt :)
Viele liebe Grüße Sylvus


Lossy eX - Mi 07.05.08 19:31

Hallo Sylvus tschuldige, dass ich so lange gebraucht habe. Ich habe es ein bisschen verpennt und kaum Zeit gehabt. Und so große fremde Projekt durchsuchen sind nicht sonderlich hoch priorisiert. ;)

Mit deinem Projekt bin ich leider so gar nicht klar gekommen. Deshalb habe ich mich mal hingesetzt und habe das mal eben selber gemacht. Ich hoffe du kannst mit dem Code noch etwas anfangen.

Zum Kompilieren benötigst du die Resource aus dem anderen Projekt.