Autor |
Beitrag |
ultra2k
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Di 28.03.06 19:18
Hallo!
Also erstmal: Mir ist kein besserer Titel eingefallen *sry*.
Worum gehts: Mittlerweile gibt es immer mehr anwendungen, bei denen die GUIs immer hochwertiger werden (siehe auch Window$ V!sta). Meine Frage ist jetz ziemlich allgemein, wie ist so etwas möglich. Ich hab schon etliche Foren abgesucht, DF eingeschlossen, um lösungsansätze zu finden. Fragen zu Transparenten Forms etc wurden auch schon häufig gestellt, aber in meinen Augen nicht konkret genug.
Auf meiner Suche sind mir einige Anwendungen ins Auge gestochen, in denen ziemlich genau das verwirklicht wurde, was ich mir vorstelle:
AveDesk
RocketDock
MobyDock
Einige kennen diese Anwendungen wahrscheinlich. Es geht auch nicht unbedingt drum eine Schnellstartleiste oder ein Programm für "Desktop Widgets" zu schreiben, sondern einfach darum Programme grafisch anspruchsvoller zu gestalten.
Ganz besonders beeindruckend finde ich die Software "AveDesk", wie gesagt, kleines Programm, mit dem sich Widgets im Apple-Style erzeugen lassen. Wenn man sich den Programmordner ansieht, findet man jede menge PNG-Dateien, die den Hintergrund, oder die Form für das jeweilige Fenster bilden.
Mein Ansatz: Ich entwerfe in Photoshop, oder sonst wo, das Bild eines Stylishen (evtl. WinAmp ähnlichen) Fensters, mit Alpha-Transparenz und schatten im PNG-Format. Wenn ich das Bild jetzt aber mit Hilfe der PNG-Image-Komponente in ein TImage lade und folgende Eigenschaften setze:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| with Form1 do begin Color := clWhite; BorderStyle := bsNone; TransparentColor := True; TransparentColorValue := clWhite; end; |
...sollte ja eigentlich eine schicke "PNG-Form" erscheinen. Es erscheint aber nur ein bild ohne AlphaBleding, mit lediglich allen Weißen bereichen durchsichtig (siehe Anhang, oben). Weiß da jemand Rat?
Die zweite Sache, über die ich noch weniger in Erfahrung bringen konnte, wäre die Animation (siehe Anhang, mitte + unten). Wie ist es möglich solche Fenster flüssig (vorrausgesetzt Problem 1 wurde gelöst ) zu bewegen, zu verzerren oder deren größe zu ändern? Ich habe mir schon sachen wie GDI+, Graphics32 etc. für Delphi angesehen. Bei MobyDock in der Beschreibung steht was von DirectX unterstützt. Wie ist sowas zu bewerkstelligen? Also, wie kann man "beschleunigt" Grafiken manipulieren?
Vielen Dank im vorraus für eventuelle antworten ^^
Moderiert von AXMD: Code- durch Delphi-Tags ersetzt
Einloggen, um Attachments anzusehen!
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
ultra2k
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Mi 29.03.06 18:33
Keiner ne idee?
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
Stefan-W
Beiträge: 475
Win 7 SP1
D2005 PE
|
Verfasst: Mi 29.03.06 20:58
also ich interessiere mich momentan auch dafür. ich versuch aber erstmal ne gescheite PNG zu erstellen.
fürs erste muss ich sagen würd ich das Form mit hilfe der Zeile
Delphi-Quelltext 1:
| Form1.Brush.Style := bsClear; |
unsichtbar machen. damit hat man keine Probleme mit irgent einer Transparenten Farbe...
ich werd die sache erstmal weiter verfolgen. und dann denk ich kann ich mir das später (am WE oder so) mal genauer ansehen
|
|
Stefan-W
Beiträge: 475
Win 7 SP1
D2005 PE
|
Verfasst: Mi 29.03.06 22:37
Also ich hab mir heut erstmal ne Komponente besorgt womit ich jetzt im normalen TImage auch PNG's laden kann damit wäre dann das Style kein Problem mehr. für die Animationen hab ich allerdings keine Idee.
Der Link zur Kompo: pngdelphi.sourceforge.net
find ich nicht mal so schlecht. jetzt muss ich nur noch weng mit GIMP ringen bis es ein paar schöne PNG's erstellt
Gut noch n schönen Abend
Tschau Stefan
|
|
Coder
Beiträge: 1383
Erhaltene Danke: 1
WinXP
D2005 PE
|
Verfasst: Mi 29.03.06 23:29
Bin gespannt wie du das hingriegst.
Beim Startscreen von Lineage werden für den Schatten die Pixel des Wallis mit dem der halbtranzparenten PNG vermischt.
Das sieht man jedoch wenn man ein anderes Fenster darunter schiebt.
Eine ständige aktualisierung der Pixel würde wohl stark auf die Performance gehen.
|
|
ultra2k
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Do 30.03.06 00:37
@ Stefan-W: hmm... stimmt! Da hab ich jetz überhaupt nich dran gedacht, das funktioniert ja wirklich.
Leider hat aber Coder recht, wenn man das fenster verschiebt, bleibt das alte Bild bestehen. Und wenn man dann noch, wie ich, eingestellt hat, dass die Fenster beim verschieben transparent dargestellt werden, isses ganz aus (und ständiges Refresh oder WM_PAINT wirkt sich vermutlich auch negativ auf die Performance aus)
Deshalb denk ich mir, da muss es doch eine bessere Lösung für geben... Nur welche?
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
alzaimar
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 30.03.06 08:13
Ich hatte mir das vor Jahren mal selbst zusammengebastelt. Im Gegensatz zum Windowseigenen Alpha-Blending kam meine Version mit wesentlich weniger CPU, dafür mit 'etwas' mehr Speicher aus:
1. Form ausblenden (als Bitmap mit Regions)
2. Screenshot
3. Form einblenden
4. Die äußeren 4-5 Pixel der Form mit dem Hintergrund (aus dem Screenshot) vermischen. Das Verhältnis von Formpixel zu Screenpixel entspricht dem Abstand vom äußeren Rand der Form. Das führt zu einem nahtlosen Übergang zwischen Hintergrund und Form.
5. Die bei (4) erzeugte Bitmap ist dann das sichtbare Image der Form.
Das wird jedesmal beim Verschieben der Form ausgeführt. Die Form verschwindet kurz, aber das geschieht im MouseDown, fällt also kaum auf.
Zu 4.: Die Randpixel sowie den Abstand zum äußeren Rand habe ich mir beim Programmstart einmalig berechnet und in ein Array gepackt (X,Y,Distance). Das sind dann so ca. 5000 pixel, und das geht recht flott. Ich habe alle Pixel in das Array gepackt, die vom Formrand maximal 4 Pixel entfernt waren. Natürlich benötigt man neben dem Formimage auch eine Maske, und mit der rechnet man die Abstände aus.
Damit auch Veränderungen auf dem Bildschirm richtig umgesetzt werden, mache ich einen kompletten Screenshot ca. 2x pro Sekunde und vergleiche den mit dem vorherigen Screenshot. Bei Veränderungen rufe ich die o.g. Routine auf. Natürlich muss man nur die unmittelbare Umgebug der Formposition prüfen.
Das hat perfekt funktioniert. Die Ränder der Form waren schön diffus, es traten keine Jaggies auf und der Effekt war mit dem von WinAmp vergleichbar. Nur besser, weil auch auf 400Mhz Büchsen uhne Performanceeinbußen lauffähig.
Ich würde den Code hier gerne posten, nur leider isser .... wech.
Das Einzige, was etwas komplizierter war, war die Erstellung des Arrays. Insgesamt betrug der Aufwand (für Schritte 1-5) ca. 200 Zeilen Code.
Alternativ kann man auch eine Maske erstellen, die nicht rein Schwarz-Weiss ist, sondern zum Rand hin langsam ins Weiss übergeht. Mit geeigneten BitBlt-Operationen mischt man dann Vordergrund (Form) mit Hintergrund (Screen). Weiss = Nur Screen, Schwarz=Nur Form, Graustufen entsprechen dem Mischverhältnis. Nur kann ich kein Assembler und meine, das meine Lösung (Array) trotzdem schneller war, als eine Lösung mit Scanline, kann mich aber irren.
_________________ Na denn, dann. Bis dann, denn.
|
|
Coder
Beiträge: 1383
Erhaltene Danke: 1
WinXP
D2005 PE
|
Verfasst: Do 30.03.06 13:11
Wird für den Screenshot die Zwischenablage benutzt?
Fänd ich sonst io.
|
|
alzaimar
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 30.03.06 19:30
Nö, einfach Screenshot in ein Bitmap und dann damit weiter rechnen. Da Du das gesamte Rendering sowieso selber bastelst, kannst Du dann ziemlich einfach Schatten und sonstewas einbauen.
_________________ Na denn, dann. Bis dann, denn.
|
|
F34r0fTh3D4rk
Beiträge: 5284
Erhaltene Danke: 27
Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
|
Verfasst: Do 30.03.06 20:32
am besten von TForm ganz die Finger lassen oder abwandeln, zb würde ich das zeichnen so lösen:
bevor das formular gezeichnet wird, erstmal den schatten zeichnen, dazu nen screenshot mit 5px abstand nach unten und zb nach rechts, also 2 quadrate machen. dann braucht man nen schnellen alphablenden algo für den schatten, die ecke unten würde ich extra machen (kleines quadrat) für die rundung.
dann das formular in die lücke zeichnen, wird das formular bewegt, oder etwas anderes passiert, wird das ganze wiederholt.
man kann auch gleich den ganzen desk screenshotten und abfragen ob sich dort was ändert und dann nen neuen screenshot machen, sollte auch kein ding sein.
|
|
Passi077
Beiträge: 125
Win XP
D7 Pers
|
Verfasst: Do 30.03.06 22:46
Hi,
also ich hab hier einen Code, der macht eigentlich genau das was du willst und zwar in Echtzeit ohne große CPU Auslastung. Funzt aber nur unter WinXP und 2000. Und ich hab ihn nur in Visual Basic.
Hab mal das Ergebnis angefügt (die VB Runtimes müssen installiert sein..).
Bei Bedarf gibt's den Code, weiß aber nicht ob man den nach Delphi übertragen kann..
Grüße
Passi
Einloggen, um Attachments anzusehen!
|
|
Stefan-W
Beiträge: 475
Win 7 SP1
D2005 PE
|
Verfasst: Do 30.03.06 22:51
@Passi077: klar immer ran mit dem Code. ich such sowas auch ( hier)
|
|
Passi077
Beiträge: 125
Win XP
D7 Pers
|
Verfasst: Do 30.03.06 22:54
Öhm ok ich mach es mir jetzt auf die schnelle noch einfach und häng einfach den gesamten Ordner an ^^
Den Code sollte man aus den Dateien per Texteditor auslesen können falls kein VB vorhanden
Einloggen, um Attachments anzusehen!
|
|
Stefan-W
Beiträge: 475
Win 7 SP1
D2005 PE
|
Verfasst: Do 30.03.06 23:02
@Passi077: Danke für den Code. Genau sowas von der Funktionsweise such ich... jetzt muss ich das nur noch übersetzen. mal sehen obs was wird
|
|
ultra2k
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Fr 31.03.06 00:24
*** müll entfernt ***
//EDIT: AHH, OHH, vergesst was ich gesagt habe, das is ja genau das Ja geil, wie siehts aus Stefan-W ich kann null VB, aber sollen wir mal probieren das zu portieren?
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
Aya
Beiträge: 1964
Erhaltene Danke: 15
MacOSX 10.6.7
Xcode / C++
|
Verfasst: Fr 31.03.06 03:33
Hi,
nachdem mich das durchaus auch interressiert hat, hab ich mich eben kurz hingesetzt und den Code portiert.. klappt einwandfrei.
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: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139:
| unit kSysTransparentForm;
interface
uses Windows, Graphics;
function InitTransparentForm: Boolean; procedure DestroyTransparentForm; function MakeTransparentForm(FormHandle: hWnd; Width, Height: Integer; imageFilename: PWideChar): Boolean;
implementation
const GdiPlusLib = 'GdiPlus.dll';
var mDC: Cardinal; mainBitmap: Cardinal; blendFunc32bpp: BLENDFUNCTION; token: Cardinal; oldBitmap: Cardinal;
type DebugEventProc = procedure(Level: Integer; Msg: Pointer); stdcall; NotificationHookProc = function(out token: DWord): Integer; stdcall; NotificationUnhookProc = procedure(token: DWord); stdcall; PGdiplusStartupInput = ^GdiplusStartupInput; GdiplusStartupInput = packed object GdiplusVersion: UInt; DebugEventCallback: DebugEventProc; SuppressBackgroundThread: Bool; SuppressExternalCodecs: Bool; procedure Init(debugEventCB: DebugEventProc = nil; supBackgroundThread: BOOL = false; supExternalCodecs: BOOL = false); end; PGdiplusStartupOutput = ^GdiplusStartupOutput; GdiplusStartupOutput = packed record NotificationHook: NotificationHookProc; NotificationUnhook: NotificationUnhookProc; end;
function GdipCreateFromHDC(hDC: HDC; var Graphics: Cardinal): Integer; stdcall; external GdiPlusLib; function GdipLoadImageFromFile(const fileName: PWideChar; var Image: Cardinal): Integer; stdcall; external GdiPlusLib; function GdipGetImageWidth(Image: Cardinal; var Width: UINT): Integer; stdcall; external GdiPlusLib; function GdipGetImageHeight(Image: Cardinal; var Height: UINT): Integer; stdcall; external GdiPlusLib; function GdipDrawImageRect(Graphics: Cardinal; Image: Cardinal; X, Y, Width, Height: Single): Integer; stdcall; external GdiPlusLib; function GdipDisposeImage(Image: Cardinal): Integer; stdcall; external GdiPlusLib; function GdipDeleteGraphics(Graphics: Cardinal): Integer; stdcall; external GdiPlusLib; function GdiplusStartup(out Token: DWord; const Input: GdiplusStartupInput; Output: PGdiplusStartupOutput): Integer; stdcall; external GdiPlusLib; procedure GdiplusShutdown(Token: DWord); stdcall; external GdiPlusLib;
function InitTransparentForm: Boolean; var GpInput: GdiplusStartupInput; GpOutput: GdiplusStartupOutput; begin GpInput.Init; Result:=GdiplusStartup(Token, GpInput, @GpOutput) = 0; end;
procedure DestroyTransparentForm; begin GdiplusShutdown(Token); SelectObject(mDC, oldBitmap); DeleteObject(mainBitmap); DeleteObject(oldBitmap); DeleteDC(mDC); end;
function MakeTransparentForm(FormHandle: hWnd; Width, Height: Integer; imageFilename: PWideChar): Boolean; var tempBitmap: BITMAPINFO; lngHeight, lngWidth: Cardinal; curWinLong: Cardinal; img: Cardinal; graphics: Cardinal; winSize: Size; srcPoint: TPoint; pvBits: Pointer; HandleDC: hDC; begin HandleDC:=GetDC(FormHandle); Result:=true; ZeroMemory(@tempBitmap, sizeof(BITMAPINFO)); with tempBitmap.bmiHeader do begin biSize:=SizeOf(BITMAPINFOHEADER); biBitCount:=32; biWidth:=Width; biHeight:=Height; biPlanes:=1; biCompression:=BI_RGB; biSizeImage:=biWidth * biHeight * 4; end; mDC:=CreateCompatibleDC(HandleDC); mainBitmap:=CreateDIBSection(mDC, tempBitmap, DIB_RGB_COLORS, pvBits, 0, 0); oldBitmap:=SelectObject(mDC, mainBitmap);
if GdipCreateFromHDC(mDC, graphics) <> 0 then Result:=false; if GdipLoadImageFromFile(imageFilename, img) <> 0 then Result:=false; if GdipGetImageWidth(img, lngWidth) <> 0 then Result:=false; if GdipGetImageHeight(img, lngHeight) <> 0 then Result:=false; if GdipDrawImageRect(graphics, img, 0, 0, lngWidth, lngHeight) <> 0 then Result:=false; curWinLong:=GetWindowLong(FormHandle, GWL_EXSTYLE); SetWindowLong(FormHandle, GWL_EXSTYLE, curWinLong or WS_EX_LAYERED); SetWindowPos(FormHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE); srcPoint.X:=0; srcPoint.Y:=0; winSize.cx:=Width; winSize.cy:=Height; with blendFunc32bpp do begin AlphaFormat:=AC_SRC_ALPHA; BlendFlags:=0; BlendOp:=AC_SRC_OVER; SourceConstantAlpha:=255; end; if GdipDisposeImage(img) <> 0 then Result:=false; if GdipDeleteGraphics(graphics) <> 0 then Result:=false; if not UpdateLayeredWindow(FormHandle, HandleDC, nil, @winSize, mDC, @srcPoint, 0, @blendFunc32bpp, ULW_ALPHA) then Result:=false; end;
procedure GdiplusStartupInput.Init(debugEventCB: DebugEventProc; supBackgroundThread, supExternalCodecs: BOOL); begin GdiplusVersion:=1; DebugEventCallback:=debugEventCB; SuppressBackgroundThread:=supBackgroundThread; SuppressExternalCodecs:=supExternalCodecs; end;
end. |
InitTransparentForm muß einmal am anfang aufgerufen werden. Liefert true zurück wenn GDI+ verfügbar ist.
MakeTransparentForm immer dann aufrufen wenn das Form transparent gemacht werden soll
DestroyTransparentForm beim beenden aufrufen um alle Variablen etc wieder freizugeben.
Viel spaß damit
Au'revoir,
Aya~
PS: Als dateinamen einfach ein PNG mit Transparenzen angeben.
_________________ Aya
I aim for my endless dreams and I know they will come true!
|
|
Stefan-W
Beiträge: 475
Win 7 SP1
D2005 PE
|
Verfasst: Fr 31.03.06 13:35
geht wunderbar! nur wie bekomme ich da ein Label oder ein Button oder sowas drauf... klapt irgentwie nicht ganz
Achja und das "MakeTransparentForm" brauch man nur einmal nach dem "InitTransparentForm" aufrufen (das langt)
|
|
ultra2k
Beiträge: 82
XP x64
D7 Enterprise
|
Verfasst: Fr 31.03.06 14:24
Also erstmal herzlichen Dank Aya!
Aber Stefan-W hat recht, sobald die Funktion aktiviert ist, verschwinden alle Buttons etc auf der Form und lassen sich auch nicht neuzeichnen...
_________________ ...wenn NULL besonders groß ist, isses schon fast wie ein bisschen EINS!
|
|
matze
Beiträge: 4613
Erhaltene Danke: 24
XP home, prof
Delphi 2009 Prof,
|
Verfasst: Fr 31.03.06 16:55
Hey Aya.
Super Code. Postest du die Unit bitte auch in der OpenSource Units Sparte, damit er leichter zu finden ist.
Danke !
_________________ In the beginning was the word.
And the word was content-type: text/plain.
|
|
Aya
Beiträge: 1964
Erhaltene Danke: 15
MacOSX 10.6.7
Xcode / C++
|
Verfasst: Fr 31.03.06 23:10
ultra2k hat folgendes geschrieben: | Aber Stefan-W hat recht, sobald die Funktion aktiviert ist, verschwinden alle Buttons etc auf der Form und lassen sich auch nicht neuzeichnen... |
Ja, das war mir dann auch irgendwann aufgefallen... aber ich hab bisher nochnicht raus wie ich es beheben kann...
Laut MSDN etc kann ein Form mit AlphaBlending keine Child-HWNDs haben, aber Labels, Images, Speedbuttons und so sollten gehen.. tun sie bei mir aber nicht..
Hab schon ewig gegoogelt.. haben auch ne menge das gleiche problem, aber nirgendwo eine lösung für das Problem..
Ich hab aber noch dashier gefunden eben:
www.am-ende.net/Delphi/AlphaEffects/
Der benutzt die selbe Methode und schafft es irgendwie Buttons etc darzustellen, aber ich find in dem Quellcode nichts woran das liegt...
Aya..
_________________ Aya
I aim for my endless dreams and I know they will come true!
|
|
|