transparente Fenster (auch semitransparent)



  • hi leute,

    ich hab mich mal hingesetzt und weiter im internet gesucht und zusammen mit dem beiden kompos von torry (TGlassy und TStainedGlass) das Ganze mal für den Builder gebastelt, allerdings ist es keine Komponente, sondern lediglich so als Code zum einbauen ins Forumlar:

    1. eine Form erzeugen (möglichst klein halten)
    2. einige Steuerelemente drauf (muss nicht sein)
    3. folgenden Code einsetzen

    // im header
    //---------------------------------------------------------------------------
    private:
            Graphics::TBitmap *ScreenBitmap;
            Graphics::TBitmap *ClientBitmap;
            Graphics::TBitmap *BackgroundBitmap;
    
            bool FormMoving;
            bool NeedRefresh;
    
            Controls::TWndMethod OldWndProc;
            void __fastcall NewWndProc(TMessage &Msg);
    
            void __fastcall PaintTransparentForm(TMessage &Msg);
            void __fastcall CreateBackground(int bHeight, int bWidth);
    //---------------------------------------------------------------------------
    
    // im cpp-file
    //---------------------------------------------------------------------------
    void __fastcall TForm1::CreateBackground(int bHeight, int bWidth)
    {
      NeedRefresh = false;
    
      BackgroundBitmap->Width  = bWidth;
      BackgroundBitmap->Height = bHeight;
    
      // hintergrund zeichnen
      BackgroundBitmap->Canvas->Pen->Style   = psClear;
      BackgroundBitmap->Canvas->Brush->Style = bsSolid;
      BackgroundBitmap->Canvas->Brush->Color = Color;
      BackgroundBitmap->Canvas->Rectangle(0, 0, bWidth, bHeight);
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::PaintTransparentForm(TMessage &Msg)
    {
      if (ScreenBitmap == NULL) return;
    
      // screenshot nur machen, wenn fenster nicht bewegt wird
      if (!FormMoving)
      {
        // fenster verstecken
        ShowWindow(Handle, SW_HIDE);
    
        // desktop als aktives fenster setzen
        SetActiveWindow(0);
    
        DWORD TicksNow  = GetTickCount();
        DWORD WaitTicks = 250;
    
        // kurz warten, bis andere fenster gezeichnet sind
        while ((GetTickCount() - TicksNow) < WaitTicks)
          Application->ProcessMessages();
    
        // einen aktuelle screenshot des bildschirms (desktops) machen
        HDC hDevice = GetDC(0);
        BitBlt(ScreenBitmap->Canvas->Handle, 0, 0, ScreenBitmap->Width, ScreenBitmap->Height, hDevice, 0, 0, SRCCOPY);
        ReleaseDC(0, hDevice);
      }
    
      // ein bitmap von der grösse des formulares erzeugen (clientbild)
      ClientBitmap              = new Graphics::TBitmap();
      ClientBitmap->Width       = ClientWidth + 1;
      ClientBitmap->Height      = ClientHeight + 1;
      ClientBitmap->PixelFormat = pf24bit;
    
      // vom bildschirmbild den ausschnitt unseres formulares
      // in das clientbild zeichnen
      ClientBitmap->Canvas->Draw(-ClientOrigin.x, -ClientOrigin.y, ScreenBitmap);
    
      if (NeedRefresh)
        CreateBackground(ClientHeight + 1, ClientWidth + 1);
    
      // transparenz setzen
      int Transparenz = 50;
      int Hoehe       = ClientHeight;
    
      // farbwerte des hintergrundes vorberechnen
      BYTE rb = GetRValue(BackgroundBitmap->Canvas->Pixels[0][0]);
      BYTE gb = GetGValue(BackgroundBitmap->Canvas->Pixels[0][0]);
      BYTE bb = GetBValue(BackgroundBitmap->Canvas->Pixels[0][0]);
    
      int rt = (100 - Transparenz) * rb;
      int gt = (100 - Transparenz) * gb;
      int bt = (100 - Transparenz) * bb;
    
      // die gesamte zeichenfläche des bitmaps verarbeiten
      // -> jede pixelfarbe neu berechnen
      for (; Hoehe--;)
      {
        // eine zeile einlesen
        RGBTRIPLE *SL     = (RGBTRIPLE *) ClientBitmap->ScanLine[Hoehe];
        int        Breite = ClientWidth;
    
        for (; Breite--;)
        {
          // einzelne farbwerte berechnen
          SL[Breite].rgbtRed   = ((Transparenz * SL[Breite].rgbtRed)   + rt) / 100;
          SL[Breite].rgbtGreen = ((Transparenz * SL[Breite].rgbtGreen) + gt) / 100;
          SL[Breite].rgbtBlue  = ((Transparenz * SL[Breite].rgbtBlue)  + bt) / 100;
        }
      }
    
      // fenster anzeigen
      ShowWindow(Handle, SW_SHOW);
    
      // geändertes clientbild auf das formular zeichnen
      PAINTSTRUCT PS;
      HDC         hPS = BeginPaint(Handle, &PS);
    
      BitBlt(hPS, 0, 0, ClientBitmap->Width, ClientBitmap->Height, ClientBitmap->Canvas->Handle, 0, 0, SRCCOPY);
    
      EndPaint(Handle, &PS);
    
      // clientbild löschen
      delete ClientBitmap;
      ClientBitmap = NULL;
    
      Msg.WParam = (int) hPS;
    }
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
    {
      // WindowProzedur austauschen (zum Abfangen und Verarbeiten der Botschaften)
      OldWndProc = WindowProc;
      WindowProc = NewWndProc;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::NewWndProc(TMessage &Msg)
    {
      // alles freigeben beim zerstören
      if (Msg.Msg == WM_DESTROY)
      {
        // bild freigeben
        if (ScreenBitmap != NULL)
        {
          delete ScreenBitmap;
          ScreenBitmap = NULL;
        }
    
        if (BackgroundBitmap != NULL)
        {
          delete BackgroundBitmap;
          BackgroundBitmap = NULL;
        }
      }
    
      // formular soll neu gezeichnet werden
      if (Msg.Msg == WM_PAINT)
      {
        PaintTransparentForm(Msg);
      }
    
      if (Msg.Msg == WM_ERASEBKGND)
      {
        Msg.Result = 1;
        return;
      }
    
      if (Msg.Msg == WM_ENTERSIZEMOVE) FormMoving = true;
      if (Msg.Msg == WM_EXITSIZEMOVE)  FormMoving = false;
    
      // wenn auflösung geändert wird
      if (Msg.Msg == WM_DISPLAYCHANGE)
      {
        if (ScreenBitmap != NULL)
        {
          ScreenBitmap->Width  = Msg.LParamLo;
          ScreenBitmap->Height = Msg.LParamHi;
    
          NeedRefresh = true;
    
          Invalidate();
        }
      }
    
      // wenn formular resized oder gemovet wird
      if (Msg.Msg == WM_WINDOWPOSCHANGED)
      {
        if (ScreenBitmap != NULL)
          Invalidate();
      }
    
      // original-windowprozedur aufrufen
      OldWndProc(Msg);
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
      FormMoving  = false;
      NeedRefresh = true;
    
      // Hintergrundbild
      BackgroundBitmap              = new Graphics::TBitmap();
      BackgroundBitmap->PixelFormat = pf24bit;
    
      // ein bitmap von der grösse der bildschirmauflösung erzeugen
      ScreenBitmap              = new Graphics::TBitmap();
      ScreenBitmap->Width       = GetSystemMetrics(SM_CXSCREEN);
      ScreenBitmap->Height      = GetSystemMetrics(SM_CYSCREEN);
      ScreenBitmap->PixelFormat = pf24bit;
    }
    //---------------------------------------------------------------------------
    

    wenn ihr das ausprobiert, werden ihr sehen, dass es beim ziehen ganz schön ruckelt und die aktualisierung nicht so richtig hinterher kommt.

    vielleicht habt ihr ja noch ein paar idee wie man die berechnungen beschleunigen kann, oder wo man gewisse daten nicht immer erneut abfragen muss. vlei gibts ja auch schnellere zeichenmethoden.

    hier mal noch ein paar zeitwerte (per GetTickCount). die werte (in millisekunden) halten sich bei mehreren versuchen in etwa die waage.

    ShowWindow                   : 1
    SetActiveWindow              : 0
    Wait                         : 250
    BitBlt (ScreenShot erstellen): 30
    ClientBitmap->Canvas->Draw   : 5
    CreateBackground             : 5
    Transparenz berechnen        : 780
    BitBlt (Client zeichnen)     : 5
    

    wie man sieht hagts so ziemlich an der eigentlichen berechnung...

    [Edit]

    • so hab mal die berechnung und schleifen ein bissel geändert, läuft jetzt um einiges schneller.

    [ Dieser Beitrag wurde am 26.02.2003 um 22:16 Uhr von Sunday editiert. ]



  • Hi! Ich habe mal ersucht das Beispiel nachzuvollziehen. Der Compiler scheitert aber an

    Controls::TWndMethod OldWndProc;
    

    mit volgender Fehlermeldung:

    [C++ Fehler] Unit1.h(23): E2316 'TWndMethod' ist kein Element von 'Controls'

    Wenn man die Zeile folgendermaßen abändert gehts:

    Classes::TWndMethod OldWndProc;
    

Anmelden zum Antworten